src/audio/SDL_audiocvt.c
author Sam Lantinga
Mon, 09 Jan 2017 11:58:01 -0800
changeset 10802 6afc9b833867
parent 10799 234f71894a52
child 10804 1502bb751ca4
permissions -rw-r--r--
We only need the first few keymaps corresponding to the following constants:
K_NORMTAB, K_SHIFTTAB, K_ALTTAB, K_ALTSHIFTTAB

In the normal case we'll load all the keymaps from the kernel, but this reduces the size of the SDL library for the fallback case when we can't get to the tty.
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@0
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
icculus@8093
    21
#include "../SDL_internal.h"
slouken@2728
    22
slouken@0
    23
/* Functions for audio drivers to perform runtime conversion of audio format */
slouken@0
    24
slouken@0
    25
#include "SDL_audio.h"
icculus@1982
    26
#include "SDL_audio_c.h"
slouken@0
    27
slouken@10773
    28
#include "SDL_loadso.h"
icculus@6281
    29
#include "SDL_assert.h"
icculus@10757
    30
#include "../SDL_dataqueue.h"
slouken@2716
    31
slouken@0
    32
/* Effectively mix right and left channels into a single channel */
icculus@1982
    33
static void SDLCALL
icculus@10793
    34
SDL_ConvertStereoToMono(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);
icculus@1982
    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
icculus@10793
    54
/* Convert from 5.1 to stereo. Average left and right, discard subwoofer. */
icculus@1982
    55
static void SDLCALL
icculus@10793
    56
SDL_Convert51ToStereo(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@10793
    62
    LOG_DEBUG_CONVERT("5.1", "stereo");
icculus@10575
    63
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
    64
icculus@10793
    65
    /* this assumes FL+FR+FC+subwoof+BL+BR layout. */
icculus@10575
    66
    for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
icculus@10793
    67
        const double front_center = (double) src[2];
icculus@10793
    68
        dst[0] = (float) ((src[0] + front_center + src[4]) / 3.0);  /* left */
icculus@10793
    69
        dst[1] = (float) ((src[1] + front_center + src[5]) / 3.0);  /* right */
icculus@1982
    70
    }
slouken@942
    71
slouken@1895
    72
    cvt->len_cvt /= 3;
slouken@1895
    73
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
    74
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
    75
    }
slouken@942
    76
}
slouken@942
    77
slouken@942
    78
icculus@10793
    79
/* Convert from 5.1 to quad */
icculus@1982
    80
static void SDLCALL
icculus@10793
    81
SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
    82
{
icculus@10575
    83
    float *dst = (float *) cvt->buf;
icculus@10575
    84
    const float *src = dst;
slouken@1895
    85
    int i;
slouken@942
    86
icculus@10793
    87
    LOG_DEBUG_CONVERT("5.1", "quad");
icculus@10575
    88
    SDL_assert(format == AUDIO_F32SYS);
slouken@1895
    89
icculus@10793
    90
    /* assumes quad is FL+FR+BL+BR layout and 5.1 is FL+FR+FC+subwoof+BL+BR */
icculus@10575
    91
    for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
icculus@10793
    92
        /* FIXME: this is a good candidate for SIMD. */
icculus@10793
    93
        const double front_center = (double) src[2];
icculus@10793
    94
        dst[0] = (float) ((src[0] + front_center) * 0.5);  /* FL */
icculus@10793
    95
        dst[1] = (float) ((src[1] + front_center) * 0.5);  /* FR */
icculus@10793
    96
        dst[2] = (float) ((src[4] + front_center) * 0.5);  /* BL */
icculus@10793
    97
        dst[3] = (float) ((src[5] + front_center) * 0.5);  /* BR */
icculus@1982
    98
    }
slouken@942
    99
icculus@1982
   100
    cvt->len_cvt /= 6;
icculus@1982
   101
    cvt->len_cvt *= 4;
slouken@1895
   102
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   103
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   104
    }
slouken@942
   105
}
slouken@0
   106
icculus@10793
   107
slouken@0
   108
/* Duplicate a mono channel to both stereo channels */
icculus@1982
   109
static void SDLCALL
icculus@10793
   110
SDL_ConvertMonoToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@0
   111
{
icculus@10575
   112
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   113
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
slouken@1895
   114
    int i;
slouken@0
   115
icculus@10575
   116
    LOG_DEBUG_CONVERT("mono", "stereo");
icculus@10575
   117
    SDL_assert(format == AUDIO_F32SYS);
slouken@0
   118
icculus@10575
   119
    for (i = cvt->len_cvt / sizeof (float); i; --i) {
icculus@10575
   120
        src--;
icculus@10575
   121
        dst -= 2;
icculus@10575
   122
        dst[0] = dst[1] = *src;
icculus@1982
   123
    }
slouken@0
   124
slouken@1895
   125
    cvt->len_cvt *= 2;
slouken@1895
   126
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   127
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   128
    }
slouken@0
   129
}
slouken@0
   130
slouken@942
   131
slouken@942
   132
/* Duplicate a stereo channel to a pseudo-5.1 stream */
icculus@1982
   133
static void SDLCALL
icculus@10793
   134
SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   135
{
slouken@1895
   136
    int i;
icculus@10575
   137
    float lf, rf, ce;
icculus@10575
   138
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   139
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 3);
slouken@942
   140
icculus@10575
   141
    LOG_DEBUG_CONVERT("stereo", "5.1");
icculus@10575
   142
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   143
icculus@10575
   144
    for (i = cvt->len_cvt / 8; i; --i) {
icculus@10575
   145
        dst -= 6;
icculus@10575
   146
        src -= 2;
icculus@10575
   147
        lf = src[0];
icculus@10575
   148
        rf = src[1];
icculus@10793
   149
        ce = (lf + rf) * 0.5f;
icculus@10793
   150
        dst[0] = lf + (lf - ce);  /* FL */
icculus@10793
   151
        dst[1] = rf + (rf - ce);  /* FR */
icculus@10793
   152
        dst[2] = ce;  /* FC */
icculus@10793
   153
        dst[3] = ce;  /* !!! FIXME: wrong! This is the subwoofer. */
icculus@10793
   154
        dst[4] = lf;  /* BL */
icculus@10793
   155
        dst[5] = rf;  /* BR */
icculus@10575
   156
    }
icculus@1982
   157
slouken@1895
   158
    cvt->len_cvt *= 3;
slouken@1895
   159
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   160
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   161
    }
slouken@942
   162
}
slouken@942
   163
slouken@942
   164
slouken@942
   165
/* Duplicate a stereo channel to a pseudo-4.0 stream */
icculus@1982
   166
static void SDLCALL
icculus@10793
   167
SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   168
{
icculus@10575
   169
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   170
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
icculus@10793
   171
    float lf, rf;
slouken@1895
   172
    int i;
slouken@942
   173
icculus@10575
   174
    LOG_DEBUG_CONVERT("stereo", "quad");
icculus@10575
   175
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   176
icculus@10575
   177
    for (i = cvt->len_cvt / 8; i; --i) {
icculus@10575
   178
        dst -= 4;
icculus@10575
   179
        src -= 2;
icculus@10575
   180
        lf = src[0];
icculus@10575
   181
        rf = src[1];
icculus@10793
   182
        dst[0] = lf;  /* FL */
icculus@10793
   183
        dst[1] = rf;  /* FR */
icculus@10793
   184
        dst[2] = lf;  /* BL */
icculus@10793
   185
        dst[3] = rf;  /* BR */
icculus@10575
   186
    }
slouken@0
   187
slouken@1895
   188
    cvt->len_cvt *= 2;
slouken@1895
   189
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   190
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   191
    }
slouken@942
   192
}
slouken@942
   193
icculus@10799
   194
static int
icculus@10799
   195
SDL_ResampleAudioSimple(const int chans, const double rate_incr,
icculus@10799
   196
                        float *last_sample, const float *inbuf,
icculus@10799
   197
                        const int inbuflen, float *outbuf, const int outbuflen)
icculus@10799
   198
{
icculus@10799
   199
    const int framelen = chans * sizeof(float);
icculus@10799
   200
    const int total = (inbuflen / framelen);
icculus@10799
   201
    const int finalpos = total - chans;
icculus@10799
   202
    const double src_incr = 1.0 / rate_incr;
icculus@10799
   203
    double idx = 0.0;
icculus@10799
   204
    float *dst = outbuf;
icculus@10799
   205
    int consumed = 0;
icculus@10799
   206
    int i;
icculus@10799
   207
icculus@10799
   208
    SDL_assert((inbuflen % framelen) == 0);
icculus@10799
   209
icculus@10799
   210
    while (consumed < total) {
icculus@10799
   211
        const int pos = ((int)idx) * chans;
icculus@10799
   212
        const float *src = &inbuf[(pos >= finalpos) ? finalpos : pos];
icculus@10799
   213
        SDL_assert(dst < (outbuf + (outbuflen / framelen)));
icculus@10799
   214
        for (i = 0; i < chans; i++) {
icculus@10799
   215
            const float val = *(src++);
icculus@10799
   216
            *(dst++) = (val + last_sample[i]) * 0.5f;
icculus@10799
   217
            last_sample[i] = val;
icculus@10799
   218
        }
icculus@10799
   219
        consumed = pos + chans;
icculus@10799
   220
        idx += src_incr;
icculus@10799
   221
    }
icculus@10799
   222
icculus@10799
   223
    return (int)((dst - outbuf) * sizeof(float));
icculus@10799
   224
}
icculus@10799
   225
slouken@0
   226
slouken@1895
   227
int
slouken@1895
   228
SDL_ConvertAudio(SDL_AudioCVT * cvt)
slouken@0
   229
{
icculus@3021
   230
    /* !!! FIXME: (cvt) should be const; stack-copy it here. */
icculus@3021
   231
    /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
icculus@3021
   232
slouken@1895
   233
    /* Make sure there's data to convert */
slouken@1895
   234
    if (cvt->buf == NULL) {
icculus@10575
   235
        return SDL_SetError("No buffer allocated for conversion");
slouken@1895
   236
    }
icculus@10575
   237
slouken@1895
   238
    /* Return okay if no conversion is necessary */
slouken@1895
   239
    cvt->len_cvt = cvt->len;
slouken@1895
   240
    if (cvt->filters[0] == NULL) {
icculus@10575
   241
        return 0;
slouken@1895
   242
    }
slouken@0
   243
slouken@1895
   244
    /* Set up the conversion and go! */
slouken@1895
   245
    cvt->filter_index = 0;
slouken@1895
   246
    cvt->filters[0] (cvt, cvt->src_format);
icculus@10575
   247
    return 0;
slouken@0
   248
}
slouken@0
   249
icculus@10575
   250
static void SDLCALL
icculus@10575
   251
SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format)
icculus@1982
   252
{
slouken@10579
   253
#if DEBUG_CONVERT
slouken@10579
   254
    printf("Converting byte order\n");
slouken@10579
   255
#endif
icculus@10575
   256
icculus@10575
   257
    switch (SDL_AUDIO_BITSIZE(format)) {
icculus@10575
   258
        #define CASESWAP(b) \
icculus@10575
   259
            case b: { \
icculus@10575
   260
                Uint##b *ptr = (Uint##b *) cvt->buf; \
icculus@10575
   261
                int i; \
icculus@10575
   262
                for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \
icculus@10575
   263
                    *ptr = SDL_Swap##b(*ptr); \
icculus@10575
   264
                } \
icculus@10575
   265
                break; \
icculus@10575
   266
            }
icculus@1982
   267
icculus@10575
   268
        CASESWAP(16);
icculus@10575
   269
        CASESWAP(32);
icculus@10575
   270
        CASESWAP(64);
icculus@10575
   271
icculus@10575
   272
        #undef CASESWAP
icculus@10575
   273
icculus@10575
   274
        default: SDL_assert(!"unhandled byteswap datatype!"); break;
icculus@10575
   275
    }
icculus@10575
   276
icculus@10575
   277
    if (cvt->filters[++cvt->filter_index]) {
icculus@10575
   278
        /* flip endian flag for data. */
icculus@10575
   279
        if (format & SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   280
            format &= ~SDL_AUDIO_MASK_ENDIAN;
icculus@10575
   281
        } else {
icculus@10575
   282
            format |= SDL_AUDIO_MASK_ENDIAN;
icculus@10575
   283
        }
icculus@10575
   284
        cvt->filters[cvt->filter_index](cvt, format);
icculus@10575
   285
    }
icculus@1982
   286
}
icculus@1982
   287
icculus@1982
   288
icculus@1982
   289
static int
icculus@10575
   290
SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt)
icculus@1982
   291
{
icculus@10575
   292
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@1982
   293
icculus@10575
   294
    if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
icculus@10575
   295
        cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
icculus@10575
   296
        retval = 1;  /* added a converter. */
icculus@10575
   297
    }
icculus@1982
   298
icculus@10575
   299
    if (!SDL_AUDIO_ISFLOAT(src_fmt)) {
icculus@10576
   300
        const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
icculus@10576
   301
        const Uint16 dst_bitsize = 32;
icculus@10575
   302
        SDL_AudioFilter filter = NULL;
icculus@10576
   303
icculus@10575
   304
        switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   305
            case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
icculus@10575
   306
            case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
icculus@10575
   307
            case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
philipp@10591
   308
            case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break;
icculus@10575
   309
            case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
icculus@10575
   310
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@1982
   311
        }
icculus@1982
   312
icculus@10575
   313
        if (!filter) {
icculus@10575
   314
            return SDL_SetError("No conversion available for these formats");
icculus@10575
   315
        }
icculus@10575
   316
icculus@1982
   317
        cvt->filters[cvt->filter_index++] = filter;
icculus@1982
   318
        if (src_bitsize < dst_bitsize) {
icculus@1982
   319
            const int mult = (dst_bitsize / src_bitsize);
icculus@1982
   320
            cvt->len_mult *= mult;
icculus@1982
   321
            cvt->len_ratio *= mult;
icculus@1982
   322
        } else if (src_bitsize > dst_bitsize) {
icculus@1982
   323
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@1982
   324
        }
icculus@10576
   325
icculus@10575
   326
        retval = 1;  /* added a converter. */
icculus@1982
   327
    }
icculus@1982
   328
icculus@10575
   329
    return retval;
icculus@1982
   330
}
icculus@1982
   331
icculus@10575
   332
static int
icculus@10575
   333
SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
slouken@2716
   334
{
icculus@10575
   335
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@10575
   336
icculus@10575
   337
    if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
icculus@10577
   338
        const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
icculus@10577
   339
        const Uint16 src_bitsize = 32;
icculus@10575
   340
        SDL_AudioFilter filter = NULL;
icculus@10575
   341
        switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   342
            case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
icculus@10575
   343
            case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
icculus@10575
   344
            case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
philipp@10591
   345
            case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break;
icculus@10575
   346
            case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
icculus@10575
   347
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@10575
   348
        }
icculus@10575
   349
icculus@10575
   350
        if (!filter) {
icculus@10575
   351
            return SDL_SetError("No conversion available for these formats");
icculus@10575
   352
        }
slouken@2716
   353
icculus@10575
   354
        cvt->filters[cvt->filter_index++] = filter;
icculus@10575
   355
        if (src_bitsize < dst_bitsize) {
icculus@10575
   356
            const int mult = (dst_bitsize / src_bitsize);
icculus@10575
   357
            cvt->len_mult *= mult;
icculus@10575
   358
            cvt->len_ratio *= mult;
icculus@10575
   359
        } else if (src_bitsize > dst_bitsize) {
icculus@10575
   360
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@10575
   361
        }
icculus@10575
   362
        retval = 1;  /* added a converter. */
icculus@10575
   363
    }
icculus@10575
   364
icculus@10575
   365
    if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
icculus@10575
   366
        cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
icculus@10575
   367
        retval = 1;  /* added a converter. */
icculus@10575
   368
    }
icculus@10575
   369
icculus@10575
   370
    return retval;
slouken@2716
   371
}
slouken@2716
   372
icculus@10799
   373
static void
icculus@10799
   374
SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format)
icculus@10799
   375
{
icculus@10799
   376
    const float *src = (const float *) cvt->buf;
icculus@10799
   377
    const int srclen = cvt->len_cvt;
icculus@10799
   378
    float *dst = (float *) (cvt->buf + srclen);
icculus@10799
   379
    const int dstlen = (cvt->len * cvt->len_mult) - srclen;
icculus@10799
   380
    SDL_bool do_simple = SDL_TRUE;
icculus@10756
   381
icculus@10799
   382
    SDL_assert(format == AUDIO_F32SYS);
icculus@10799
   383
icculus@10799
   384
#ifdef HAVE_LIBSAMPLERATE_H
icculus@10799
   385
    if (SRC_available) {
icculus@10799
   386
        int result = 0;
icculus@10799
   387
        SRC_STATE *state = SRC_src_new(SRC_SINC_FASTEST, chans, &result);
icculus@10799
   388
        if (state) {
icculus@10799
   389
            const int framelen = sizeof(float) * chans;
icculus@10799
   390
            SRC_DATA data;
icculus@10799
   391
icculus@10799
   392
            data.data_in = (float *)src; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
icculus@10799
   393
            data.input_frames = srclen / framelen;
icculus@10799
   394
            data.input_frames_used = 0;
icculus@10799
   395
icculus@10799
   396
            data.data_out = dst;
icculus@10799
   397
            data.output_frames = dstlen / framelen;
icculus@10799
   398
icculus@10799
   399
            data.end_of_input = 0;
icculus@10799
   400
            data.src_ratio = cvt->rate_incr;
icculus@10799
   401
icculus@10799
   402
            result = SRC_src_process(state, &data);
icculus@10799
   403
            SDL_assert(result == 0);  /* what to do if this fails? Can it fail? */
icculus@10799
   404
icculus@10799
   405
            /* What to do if this fails...? */
icculus@10799
   406
            SDL_assert(data.input_frames_used == data.input_frames);
icculus@10799
   407
icculus@10799
   408
            SRC_src_delete(state);
icculus@10799
   409
            cvt->len_cvt = data.output_frames_gen * (sizeof(float) * chans);
icculus@10799
   410
            do_simple = SDL_FALSE;
icculus@10799
   411
        }
icculus@10799
   412
icculus@10799
   413
        /* failed to create state? Fall back to simple method. */
icculus@10799
   414
    }
icculus@10799
   415
#endif
icculus@10799
   416
icculus@10799
   417
    if (do_simple) {
icculus@10799
   418
        float state[8];
icculus@10799
   419
        int i;
icculus@10799
   420
icculus@10799
   421
        for (i = 0; i < chans; i++) {
icculus@10799
   422
            state[i] = src[i];
icculus@10799
   423
        }
icculus@10799
   424
icculus@10799
   425
        cvt->len_cvt = SDL_ResampleAudioSimple(chans, cvt->rate_incr, state, src, srclen, dst, dstlen);
icculus@10799
   426
    }
icculus@10799
   427
icculus@10799
   428
    SDL_memcpy(cvt->buf, dst, cvt->len_cvt);
icculus@10799
   429
    if (cvt->filters[++cvt->filter_index]) {
icculus@10799
   430
        cvt->filters[cvt->filter_index](cvt, format);
icculus@10799
   431
    }
icculus@10799
   432
}
icculus@10799
   433
icculus@10799
   434
/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't
icculus@10799
   435
   !!! FIXME:  store channel info, so we have to have function entry
icculus@10799
   436
   !!! FIXME:  points for each supported channel count and multiple
icculus@10799
   437
   !!! FIXME:  vs arbitrary. When we rev the ABI, clean this up. */
icculus@10756
   438
#define RESAMPLER_FUNCS(chans) \
icculus@10756
   439
    static void SDLCALL \
icculus@10799
   440
    SDL_ResampleCVT_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10799
   441
        SDL_ResampleCVT(cvt, chans, format); \
icculus@10756
   442
    }
icculus@10756
   443
RESAMPLER_FUNCS(1)
icculus@10756
   444
RESAMPLER_FUNCS(2)
icculus@10756
   445
RESAMPLER_FUNCS(4)
icculus@10756
   446
RESAMPLER_FUNCS(6)
icculus@10756
   447
RESAMPLER_FUNCS(8)
icculus@10756
   448
#undef RESAMPLER_FUNCS
icculus@10756
   449
icculus@10799
   450
static SDL_AudioFilter
icculus@10799
   451
ChooseCVTResampler(const int dst_channels)
slouken@2716
   452
{
icculus@10799
   453
    switch (dst_channels) {
icculus@10799
   454
        case 1: return SDL_ResampleCVT_c1;
icculus@10799
   455
        case 2: return SDL_ResampleCVT_c2;
icculus@10799
   456
        case 4: return SDL_ResampleCVT_c4;
icculus@10799
   457
        case 6: return SDL_ResampleCVT_c6;
icculus@10799
   458
        case 8: return SDL_ResampleCVT_c8;
icculus@10799
   459
        default: break;
slouken@2716
   460
    }
slouken@2716
   461
icculus@10799
   462
    return NULL;
icculus@10756
   463
}
icculus@10575
   464
icculus@3021
   465
static int
icculus@10756
   466
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
icculus@10756
   467
                          const int src_rate, const int dst_rate)
icculus@3021
   468
{
icculus@10756
   469
    SDL_AudioFilter filter;
slouken@2716
   470
icculus@10756
   471
    if (src_rate == dst_rate) {
icculus@10756
   472
        return 0;  /* no conversion necessary. */
slouken@2716
   473
    }
slouken@2716
   474
icculus@10799
   475
    filter = ChooseCVTResampler(dst_channels);
icculus@10756
   476
    if (filter == NULL) {
icculus@10756
   477
        return SDL_SetError("No conversion available for these rates");
icculus@10756
   478
    }
icculus@10756
   479
icculus@10756
   480
    /* Update (cvt) with filter details... */
icculus@10756
   481
    cvt->filters[cvt->filter_index++] = filter;
icculus@10756
   482
    if (src_rate < dst_rate) {
icculus@10756
   483
        const double mult = ((double) dst_rate) / ((double) src_rate);
icculus@10756
   484
        cvt->len_mult *= (int) SDL_ceil(mult);
icculus@10756
   485
        cvt->len_ratio *= mult;
icculus@10756
   486
    } else {
icculus@10756
   487
        cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
icculus@10756
   488
    }
icculus@10756
   489
icculus@10799
   490
    /* the buffer is big enough to hold the destination now, but
icculus@10799
   491
       we need it large enough to hold a separate scratch buffer. */
icculus@10799
   492
    cvt->len_mult *= 2;
icculus@10799
   493
icculus@10756
   494
    return 1;               /* added a converter. */
slouken@2716
   495
}
icculus@1982
   496
icculus@1982
   497
icculus@1982
   498
/* Creates a set of audio filters to convert from one format to another.
icculus@1982
   499
   Returns -1 if the format conversion is not supported, 0 if there's
icculus@1982
   500
   no conversion needed, or 1 if the audio filter is set up.
slouken@0
   501
*/
slouken@1895
   502
slouken@1895
   503
int
slouken@1895
   504
SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
icculus@1982
   505
                  SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
icculus@1982
   506
                  SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
slouken@0
   507
{
aschiffler@6819
   508
    /* Sanity check target pointer */
aschiffler@6819
   509
    if (cvt == NULL) {
icculus@7037
   510
        return SDL_InvalidParamError("cvt");
aschiffler@6819
   511
    }
slouken@7191
   512
slouken@10767
   513
    /* Make sure we zero out the audio conversion before error checking */
slouken@10767
   514
    SDL_zerop(cvt);
slouken@10767
   515
slouken@3491
   516
    /* there are no unsigned types over 16 bits, so catch this up front. */
icculus@1982
   517
    if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
icculus@7037
   518
        return SDL_SetError("Invalid source format");
icculus@1982
   519
    }
icculus@1982
   520
    if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
icculus@7037
   521
        return SDL_SetError("Invalid destination format");
icculus@1982
   522
    }
icculus@3021
   523
icculus@3021
   524
    /* prevent possible divisions by zero, etc. */
aschiffler@6819
   525
    if ((src_channels == 0) || (dst_channels == 0)) {
icculus@7037
   526
        return SDL_SetError("Source or destination channels is zero");
aschiffler@6819
   527
    }
icculus@3021
   528
    if ((src_rate == 0) || (dst_rate == 0)) {
icculus@7037
   529
        return SDL_SetError("Source or destination rate is zero");
icculus@3021
   530
    }
slouken@10579
   531
#if DEBUG_CONVERT
icculus@1982
   532
    printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
slouken@1985
   533
           src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
slouken@1985
   534
#endif
icculus@1982
   535
slouken@1895
   536
    /* Start off with no conversion necessary */
icculus@1982
   537
    cvt->src_format = src_fmt;
icculus@1982
   538
    cvt->dst_format = dst_fmt;
slouken@1895
   539
    cvt->needed = 0;
slouken@1895
   540
    cvt->filter_index = 0;
slouken@1895
   541
    cvt->filters[0] = NULL;
slouken@1895
   542
    cvt->len_mult = 1;
slouken@1895
   543
    cvt->len_ratio = 1.0;
icculus@3021
   544
    cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
slouken@1895
   545
icculus@10575
   546
    /* Type conversion goes like this now:
icculus@10575
   547
        - byteswap to CPU native format first if necessary.
icculus@10575
   548
        - convert to native Float32 if necessary.
icculus@10575
   549
        - resample and change channel count if necessary.
icculus@10575
   550
        - convert back to native format.
icculus@10575
   551
        - byteswap back to foreign format if necessary.
icculus@10575
   552
icculus@10575
   553
       The expectation is we can process data faster in float32
icculus@10575
   554
       (possibly with SIMD), and making several passes over the same
icculus@10756
   555
       buffer is likely to be CPU cache-friendly, avoiding the
icculus@10575
   556
       biggest performance hit in modern times. Previously we had
icculus@10575
   557
       (script-generated) custom converters for every data type and
icculus@10575
   558
       it was a bloat on SDL compile times and final library size. */
icculus@10575
   559
slouken@10767
   560
    /* see if we can skip float conversion entirely. */
slouken@10767
   561
    if (src_rate == dst_rate && src_channels == dst_channels) {
slouken@10767
   562
        if (src_fmt == dst_fmt) {
slouken@10767
   563
            return 0;
slouken@10767
   564
        }
slouken@10767
   565
slouken@10767
   566
        /* just a byteswap needed? */
slouken@10767
   567
        if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) {
slouken@10767
   568
            cvt->filters[cvt->filter_index++] = SDL_Convert_Byteswap;
slouken@10767
   569
            cvt->needed = 1;
slouken@10767
   570
            return 1;
slouken@10767
   571
        }
icculus@10575
   572
    }
icculus@10575
   573
icculus@1982
   574
    /* Convert data types, if necessary. Updates (cvt). */
slouken@10767
   575
    if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) {
slouken@1985
   576
        return -1;              /* shouldn't happen, but just in case... */
icculus@3021
   577
    }
slouken@0
   578
icculus@1982
   579
    /* Channel conversion */
slouken@1895
   580
    if (src_channels != dst_channels) {
slouken@1895
   581
        if ((src_channels == 1) && (dst_channels > 1)) {
icculus@10793
   582
            cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
slouken@1895
   583
            cvt->len_mult *= 2;
slouken@1895
   584
            src_channels = 2;
slouken@1895
   585
            cvt->len_ratio *= 2;
slouken@1895
   586
        }
slouken@1895
   587
        if ((src_channels == 2) && (dst_channels == 6)) {
icculus@10793
   588
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereoTo51;
slouken@1895
   589
            src_channels = 6;
slouken@1895
   590
            cvt->len_mult *= 3;
slouken@1895
   591
            cvt->len_ratio *= 3;
slouken@1895
   592
        }
slouken@1895
   593
        if ((src_channels == 2) && (dst_channels == 4)) {
icculus@10793
   594
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToQuad;
slouken@1895
   595
            src_channels = 4;
slouken@1895
   596
            cvt->len_mult *= 2;
slouken@1895
   597
            cvt->len_ratio *= 2;
slouken@1895
   598
        }
slouken@1895
   599
        while ((src_channels * 2) <= dst_channels) {
icculus@10793
   600
            cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
slouken@1895
   601
            cvt->len_mult *= 2;
slouken@1895
   602
            src_channels *= 2;
slouken@1895
   603
            cvt->len_ratio *= 2;
slouken@1895
   604
        }
slouken@1895
   605
        if ((src_channels == 6) && (dst_channels <= 2)) {
icculus@10793
   606
            cvt->filters[cvt->filter_index++] = SDL_Convert51ToStereo;
slouken@1895
   607
            src_channels = 2;
slouken@1895
   608
            cvt->len_ratio /= 3;
slouken@1895
   609
        }
slouken@1895
   610
        if ((src_channels == 6) && (dst_channels == 4)) {
icculus@10793
   611
            cvt->filters[cvt->filter_index++] = SDL_Convert51ToQuad;
slouken@1895
   612
            src_channels = 4;
slouken@1895
   613
            cvt->len_ratio /= 2;
slouken@1895
   614
        }
slouken@1895
   615
        /* This assumes that 4 channel audio is in the format:
slouken@1895
   616
           Left {front/back} + Right {front/back}
slouken@1895
   617
           so converting to L/R stereo works properly.
slouken@1895
   618
         */
slouken@1895
   619
        while (((src_channels % 2) == 0) &&
slouken@1895
   620
               ((src_channels / 2) >= dst_channels)) {
icculus@10793
   621
            cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToMono;
slouken@1895
   622
            src_channels /= 2;
slouken@1895
   623
            cvt->len_ratio /= 2;
slouken@1895
   624
        }
slouken@1895
   625
        if (src_channels != dst_channels) {
slouken@1895
   626
            /* Uh oh.. */ ;
slouken@1895
   627
        }
slouken@1895
   628
    }
slouken@0
   629
icculus@3021
   630
    /* Do rate conversion, if necessary. Updates (cvt). */
slouken@10767
   631
    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
icculus@3021
   632
        return -1;              /* shouldn't happen, but just in case... */
slouken@2716
   633
    }
slouken@2716
   634
icculus@10756
   635
    /* Move to final data type. */
slouken@10767
   636
    if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) {
icculus@10575
   637
        return -1;              /* shouldn't happen, but just in case... */
slouken@1895
   638
    }
icculus@10575
   639
icculus@10575
   640
    cvt->needed = (cvt->filter_index != 0);
slouken@1895
   641
    return (cvt->needed);
slouken@1895
   642
}
slouken@0
   643
slouken@10773
   644
typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen);
slouken@10773
   645
typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream);
slouken@10773
   646
typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream);
icculus@10757
   647
icculus@10757
   648
struct SDL_AudioStream
icculus@10757
   649
{
icculus@10757
   650
    SDL_AudioCVT cvt_before_resampling;
icculus@10757
   651
    SDL_AudioCVT cvt_after_resampling;
icculus@10757
   652
    SDL_DataQueue *queue;
icculus@10757
   653
    Uint8 *work_buffer;
icculus@10757
   654
    int work_buffer_len;
icculus@10757
   655
    Uint8 *resample_buffer;
icculus@10757
   656
    int resample_buffer_len;
icculus@10757
   657
    int src_sample_frame_size;
icculus@10757
   658
    SDL_AudioFormat src_format;
icculus@10757
   659
    Uint8 src_channels;
icculus@10757
   660
    int src_rate;
icculus@10757
   661
    int dst_sample_frame_size;
icculus@10757
   662
    SDL_AudioFormat dst_format;
icculus@10757
   663
    Uint8 dst_channels;
icculus@10757
   664
    int dst_rate;
icculus@10757
   665
    double rate_incr;
icculus@10757
   666
    Uint8 pre_resample_channels;
slouken@10773
   667
    int packetlen;
slouken@10773
   668
    void *resampler_state;
slouken@10773
   669
    SDL_ResampleAudioStreamFunc resampler_func;
slouken@10773
   670
    SDL_ResetAudioStreamResamplerFunc reset_resampler_func;
slouken@10773
   671
    SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func;
slouken@10773
   672
};
slouken@10773
   673
slouken@10777
   674
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
   675
static int
slouken@10773
   676
SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
slouken@10773
   677
{
icculus@10799
   678
    const int framelen = sizeof(float) * stream->pre_resample_channels;
icculus@10790
   679
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
   680
    SRC_DATA data;
slouken@10773
   681
    int result;
slouken@10773
   682
slouken@10777
   683
    data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
icculus@10799
   684
    data.input_frames = inbuflen / framelen;
slouken@10773
   685
    data.input_frames_used = 0;
slouken@10773
   686
slouken@10773
   687
    data.data_out = outbuf;
icculus@10799
   688
    data.output_frames = outbuflen / framelen;
slouken@10773
   689
slouken@10773
   690
    data.end_of_input = 0;
slouken@10773
   691
    data.src_ratio = stream->rate_incr;
slouken@10773
   692
icculus@10790
   693
    result = SRC_src_process(state, &data);
slouken@10773
   694
    if (result != 0) {
icculus@10790
   695
        SDL_SetError("src_process() failed: %s", SRC_src_strerror(result));
slouken@10773
   696
        return 0;
slouken@10773
   697
    }
slouken@10773
   698
slouken@10773
   699
    /* If this fails, we need to store them off somewhere */
slouken@10773
   700
    SDL_assert(data.input_frames_used == data.input_frames);
slouken@10773
   701
slouken@10773
   702
    return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels);
slouken@10773
   703
}
slouken@10773
   704
slouken@10773
   705
static void
slouken@10773
   706
SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
   707
{
icculus@10790
   708
    SRC_src_reset((SRC_STATE *)stream->resampler_state);
slouken@10773
   709
}
slouken@10773
   710
slouken@10773
   711
static void
slouken@10773
   712
SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
   713
{
icculus@10790
   714
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
   715
    if (state) {
icculus@10790
   716
        SRC_src_delete(state);
slouken@10773
   717
    }
slouken@10773
   718
slouken@10773
   719
    stream->resampler_state = NULL;
slouken@10773
   720
    stream->resampler_func = NULL;
slouken@10773
   721
    stream->reset_resampler_func = NULL;
slouken@10773
   722
    stream->cleanup_resampler_func = NULL;
slouken@10773
   723
}
slouken@10773
   724
slouken@10773
   725
static SDL_bool
slouken@10773
   726
SetupLibSampleRateResampling(SDL_AudioStream *stream)
slouken@10773
   727
{
icculus@10790
   728
    int result = 0;
icculus@10790
   729
    SRC_STATE *state = NULL;
slouken@10773
   730
icculus@10790
   731
    if (SRC_available) {
icculus@10790
   732
        state = SRC_src_new(SRC_SINC_FASTEST, stream->pre_resample_channels, &result);
icculus@10790
   733
        if (!state) {
icculus@10790
   734
            SDL_SetError("src_new() failed: %s", SRC_src_strerror(result));
icculus@10790
   735
        }
slouken@10773
   736
    }
slouken@10773
   737
icculus@10790
   738
    if (!state) {
icculus@10790
   739
        SDL_CleanupAudioStreamResampler_SRC(stream);
slouken@10773
   740
        return SDL_FALSE;
slouken@10773
   741
    }
slouken@10773
   742
slouken@10773
   743
    stream->resampler_state = state;
slouken@10773
   744
    stream->resampler_func = SDL_ResampleAudioStream_SRC;
slouken@10773
   745
    stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC;
slouken@10773
   746
    stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC;
slouken@10773
   747
slouken@10773
   748
    return SDL_TRUE;
slouken@10773
   749
}
icculus@10790
   750
#endif /* HAVE_LIBSAMPLERATE_H */
slouken@10773
   751
slouken@10773
   752
slouken@10773
   753
typedef struct
slouken@10773
   754
{
icculus@10757
   755
    SDL_bool resampler_seeded;
icculus@10757
   756
    float resampler_state[8];
slouken@10773
   757
} SDL_AudioStreamResamplerState;
slouken@10773
   758
slouken@10773
   759
static int
slouken@10773
   760
SDL_ResampleAudioStream(SDL_AudioStream *stream, const float *inbuf, const int inbuflen, float *outbuf, const int outbuflen)
slouken@10773
   761
{
slouken@10773
   762
    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
slouken@10773
   763
    const int chans = (int)stream->pre_resample_channels;
slouken@10773
   764
icculus@10799
   765
    SDL_assert(chans <= SDL_arraysize(state->resampler_state));
slouken@10773
   766
slouken@10773
   767
    if (!state->resampler_seeded) {
icculus@10799
   768
        int i;
slouken@10773
   769
        for (i = 0; i < chans; i++) {
slouken@10773
   770
            state->resampler_state[i] = inbuf[i];
slouken@10773
   771
        }
slouken@10773
   772
        state->resampler_seeded = SDL_TRUE;
slouken@10773
   773
    }
slouken@10773
   774
icculus@10799
   775
    return SDL_ResampleAudioSimple(chans, stream->rate_incr, state->resampler_state, inbuf, inbuflen, outbuf, outbuflen);
slouken@10773
   776
}
slouken@10773
   777
slouken@10773
   778
static void
slouken@10773
   779
SDL_ResetAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
   780
{
slouken@10773
   781
    SDL_AudioStreamResamplerState *state = (SDL_AudioStreamResamplerState*)stream->resampler_state;
slouken@10773
   782
    state->resampler_seeded = SDL_FALSE;
slouken@10773
   783
}
slouken@10773
   784
slouken@10773
   785
static void
slouken@10773
   786
SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
   787
{
slouken@10773
   788
    SDL_free(stream->resampler_state);
slouken@10773
   789
}
icculus@10757
   790
icculus@10789
   791
SDL_AudioStream *
icculus@10789
   792
SDL_NewAudioStream(const SDL_AudioFormat src_format,
icculus@10789
   793
                   const Uint8 src_channels,
icculus@10789
   794
                   const int src_rate,
icculus@10789
   795
                   const SDL_AudioFormat dst_format,
icculus@10789
   796
                   const Uint8 dst_channels,
icculus@10789
   797
                   const int dst_rate)
icculus@10757
   798
{
icculus@10757
   799
    const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
icculus@10757
   800
    Uint8 pre_resample_channels;
icculus@10757
   801
    SDL_AudioStream *retval;
icculus@10757
   802
icculus@10757
   803
    retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
icculus@10757
   804
    if (!retval) {
icculus@10757
   805
        return NULL;
icculus@10757
   806
    }
icculus@10757
   807
icculus@10757
   808
    /* If increasing channels, do it after resampling, since we'd just
icculus@10757
   809
       do more work to resample duplicate channels. If we're decreasing, do
icculus@10757
   810
       it first so we resample the interpolated data instead of interpolating
icculus@10757
   811
       the resampled data (!!! FIXME: decide if that works in practice, though!). */
icculus@10757
   812
    pre_resample_channels = SDL_min(src_channels, dst_channels);
icculus@10757
   813
icculus@10757
   814
    retval->src_sample_frame_size = SDL_AUDIO_BITSIZE(src_format) * src_channels;
icculus@10757
   815
    retval->src_format = src_format;
icculus@10757
   816
    retval->src_channels = src_channels;
icculus@10757
   817
    retval->src_rate = src_rate;
icculus@10757
   818
    retval->dst_sample_frame_size = SDL_AUDIO_BITSIZE(dst_format) * dst_channels;
icculus@10757
   819
    retval->dst_format = dst_format;
icculus@10757
   820
    retval->dst_channels = dst_channels;
icculus@10757
   821
    retval->dst_rate = dst_rate;
icculus@10757
   822
    retval->pre_resample_channels = pre_resample_channels;
icculus@10757
   823
    retval->packetlen = packetlen;
icculus@10757
   824
    retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
icculus@10757
   825
icculus@10757
   826
    /* Not resampling? It's an easy conversion (and maybe not even that!). */
icculus@10757
   827
    if (src_rate == dst_rate) {
icculus@10757
   828
        retval->cvt_before_resampling.needed = SDL_FALSE;
icculus@10757
   829
        retval->cvt_before_resampling.len_mult = 1;
slouken@10773
   830
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
   831
            SDL_FreeAudioStream(retval);
icculus@10757
   832
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
   833
        }
icculus@10757
   834
    } else {
icculus@10757
   835
        /* Don't resample at first. Just get us to Float32 format. */
icculus@10757
   836
        /* !!! FIXME: convert to int32 on devices without hardware float. */
slouken@10773
   837
        if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) {
slouken@10773
   838
            SDL_FreeAudioStream(retval);
icculus@10757
   839
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
   840
        }
icculus@10757
   841
slouken@10777
   842
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
   843
        SetupLibSampleRateResampling(retval);
slouken@10773
   844
#endif
slouken@10773
   845
slouken@10773
   846
        if (!retval->resampler_func) {
slouken@10773
   847
            retval->resampler_state = SDL_calloc(1, sizeof(SDL_AudioStreamResamplerState));
slouken@10773
   848
            if (!retval->resampler_state) {
slouken@10773
   849
                SDL_FreeAudioStream(retval);
slouken@10773
   850
                SDL_OutOfMemory();
slouken@10773
   851
                return NULL;
slouken@10773
   852
            }
slouken@10773
   853
            retval->resampler_func = SDL_ResampleAudioStream;
slouken@10773
   854
            retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
slouken@10773
   855
            retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
slouken@10773
   856
        }
slouken@10773
   857
icculus@10757
   858
        /* Convert us to the final format after resampling. */
slouken@10773
   859
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
   860
            SDL_FreeAudioStream(retval);
icculus@10757
   861
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
   862
        }
icculus@10757
   863
    }
icculus@10757
   864
icculus@10757
   865
    retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
icculus@10757
   866
    if (!retval->queue) {
slouken@10773
   867
        SDL_FreeAudioStream(retval);
icculus@10757
   868
        return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
icculus@10757
   869
    }
icculus@10757
   870
icculus@10757
   871
    return retval;
icculus@10757
   872
}
icculus@10757
   873
icculus@10757
   874
static Uint8 *
icculus@10757
   875
EnsureBufferSize(Uint8 **buf, int *len, const int newlen)
icculus@10757
   876
{
icculus@10757
   877
    if (*len < newlen) {
icculus@10757
   878
        void *ptr = SDL_realloc(*buf, newlen);
icculus@10757
   879
        if (!ptr) {
icculus@10757
   880
            SDL_OutOfMemory();
icculus@10757
   881
            return NULL;
icculus@10757
   882
        }
icculus@10757
   883
        *buf = (Uint8 *) ptr;
icculus@10757
   884
        *len = newlen;
icculus@10757
   885
    }
icculus@10757
   886
    return *buf;
icculus@10757
   887
}
icculus@10757
   888
icculus@10757
   889
int
icculus@10757
   890
SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
icculus@10757
   891
{
icculus@10757
   892
    int buflen = (int) _buflen;
icculus@10757
   893
icculus@10757
   894
    if (!stream) {
icculus@10757
   895
        return SDL_InvalidParamError("stream");
icculus@10757
   896
    } else if (!buf) {
icculus@10757
   897
        return SDL_InvalidParamError("buf");
icculus@10757
   898
    } else if (buflen == 0) {
icculus@10757
   899
        return 0;  /* nothing to do. */
icculus@10757
   900
    } else if ((buflen % stream->src_sample_frame_size) != 0) {
icculus@10757
   901
        return SDL_SetError("Can't add partial sample frames");
icculus@10757
   902
    }
icculus@10757
   903
icculus@10757
   904
    if (stream->cvt_before_resampling.needed) {
icculus@10757
   905
        const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
icculus@10757
   906
        Uint8 *workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
icculus@10757
   907
        if (workbuf == NULL) {
icculus@10757
   908
            return -1;  /* probably out of memory. */
icculus@10757
   909
        }
icculus@10757
   910
        SDL_memcpy(workbuf, buf, buflen);
icculus@10757
   911
        stream->cvt_before_resampling.buf = workbuf;
icculus@10757
   912
        stream->cvt_before_resampling.len = buflen;
icculus@10757
   913
        if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
icculus@10757
   914
            return -1;   /* uhoh! */
icculus@10757
   915
        }
icculus@10757
   916
        buf = workbuf;
icculus@10757
   917
        buflen = stream->cvt_before_resampling.len_cvt;
icculus@10757
   918
    }
icculus@10757
   919
icculus@10757
   920
    if (stream->dst_rate != stream->src_rate) {
icculus@10757
   921
        const int workbuflen = buflen * ((int) SDL_ceil(stream->rate_incr));
icculus@10757
   922
        float *workbuf = (float *) EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
icculus@10757
   923
        if (workbuf == NULL) {
icculus@10757
   924
            return -1;  /* probably out of memory. */
icculus@10757
   925
        }
slouken@10773
   926
        buflen = stream->resampler_func(stream, (float *) buf, buflen, workbuf, workbuflen);
icculus@10757
   927
        buf = workbuf;
icculus@10757
   928
    }
icculus@10757
   929
icculus@10757
   930
    if (stream->cvt_after_resampling.needed) {
icculus@10757
   931
        const int workbuflen = buflen * stream->cvt_before_resampling.len_mult;  /* will be "* 1" if not needed */
icculus@10757
   932
        Uint8 *workbuf;
icculus@10757
   933
icculus@10757
   934
        if (buf == stream->resample_buffer) {
icculus@10757
   935
            workbuf = EnsureBufferSize(&stream->resample_buffer, &stream->resample_buffer_len, workbuflen);
icculus@10757
   936
        } else {
icculus@10757
   937
            const int inplace = (buf == stream->work_buffer);
icculus@10757
   938
            workbuf = EnsureBufferSize(&stream->work_buffer, &stream->work_buffer_len, workbuflen);
icculus@10757
   939
            if (workbuf && !inplace) {
icculus@10757
   940
                SDL_memcpy(workbuf, buf, buflen);
icculus@10757
   941
            }
icculus@10757
   942
        }
icculus@10757
   943
icculus@10757
   944
        if (workbuf == NULL) {
icculus@10757
   945
            return -1;  /* probably out of memory. */
icculus@10757
   946
        }
icculus@10757
   947
icculus@10757
   948
        stream->cvt_after_resampling.buf = workbuf;
icculus@10757
   949
        stream->cvt_after_resampling.len = buflen;
icculus@10757
   950
        if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
icculus@10757
   951
            return -1;   /* uhoh! */
icculus@10757
   952
        }
icculus@10757
   953
        buf = workbuf;
icculus@10757
   954
        buflen = stream->cvt_after_resampling.len_cvt;
icculus@10757
   955
    }
icculus@10757
   956
icculus@10757
   957
    return SDL_WriteToDataQueue(stream->queue, buf, buflen);
icculus@10757
   958
}
icculus@10757
   959
icculus@10757
   960
void
icculus@10757
   961
SDL_AudioStreamClear(SDL_AudioStream *stream)
icculus@10757
   962
{
icculus@10757
   963
    if (!stream) {
icculus@10757
   964
        SDL_InvalidParamError("stream");
icculus@10757
   965
    } else {
icculus@10757
   966
        SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
icculus@10776
   967
        if (stream->reset_resampler_func) {
icculus@10776
   968
            stream->reset_resampler_func(stream);
icculus@10776
   969
        }
icculus@10757
   970
    }
icculus@10757
   971
}
icculus@10757
   972
icculus@10757
   973
icculus@10757
   974
/* get converted/resampled data from the stream */
icculus@10757
   975
int
icculus@10764
   976
SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, const Uint32 len)
icculus@10757
   977
{
icculus@10757
   978
    if (!stream) {
icculus@10757
   979
        return SDL_InvalidParamError("stream");
icculus@10757
   980
    } else if (!buf) {
icculus@10757
   981
        return SDL_InvalidParamError("buf");
icculus@10757
   982
    } else if (len == 0) {
icculus@10757
   983
        return 0;  /* nothing to do. */
icculus@10757
   984
    } else if ((len % stream->dst_sample_frame_size) != 0) {
icculus@10757
   985
        return SDL_SetError("Can't request partial sample frames");
icculus@10757
   986
    }
icculus@10757
   987
icculus@10764
   988
    return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
icculus@10757
   989
}
icculus@10757
   990
icculus@10757
   991
/* number of converted/resampled bytes available */
icculus@10757
   992
int
icculus@10757
   993
SDL_AudioStreamAvailable(SDL_AudioStream *stream)
icculus@10757
   994
{
icculus@10757
   995
    return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
icculus@10757
   996
}
icculus@10757
   997
icculus@10757
   998
/* dispose of a stream */
icculus@10757
   999
void
icculus@10757
  1000
SDL_FreeAudioStream(SDL_AudioStream *stream)
icculus@10757
  1001
{
icculus@10757
  1002
    if (stream) {
slouken@10773
  1003
        if (stream->cleanup_resampler_func) {
slouken@10773
  1004
            stream->cleanup_resampler_func(stream);
slouken@10773
  1005
        }
icculus@10757
  1006
        SDL_FreeDataQueue(stream->queue);
icculus@10757
  1007
        SDL_free(stream->work_buffer);
icculus@10757
  1008
        SDL_free(stream->resample_buffer);
icculus@10757
  1009
        SDL_free(stream);
icculus@10757
  1010
    }
icculus@10757
  1011
}
icculus@10757
  1012
icculus@10575
  1013
/* vi: set ts=4 sw=4 expandtab: */
slouken@2716
  1014