src/audio/SDL_audiocvt.c
author Sam Lantinga
Sat, 08 Jun 2019 18:22:18 -0700
changeset 12803 70d338e248c8
parent 12503 806492103856
child 12807 a39d8cdf50f4
permissions -rw-r--r--
Fixed bug 4294 - Audio: perform more validation on conversion request

janisozaur

There are many cases which are not able to be handled by SDL's audio conversion routines, including too low (negative) rate, too high rate (impossible to allocate).

This patch aims to report such issues early and handle others in a graceful manner. The "INT32_MAX / RESAMPLER_SAMPLES_PER_ZERO_CROSSING" value is the conservative approach in terms of what can _technically_ be supported, but its value is 4'194'303, or just shy of 4.2MHz. I highly doubt any sane person would use such rates, especially in SDL2, so I would like to drive this limit further down, but would need some assistance to do that, as doing so would have to introduce an arbitrary value. Are you OK with such approach? What would a good value be? Wikipedia (https://en.wikipedia.org/wiki/High-resolution_audio) lists 96kHz as the highest sampling rate in use, even if I quadruple it for a good measure, to 384kHz it's still an order of magnitude lower than 4MHz.
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@12503
     3
  Copyright (C) 1997-2019 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@11641
    25
/* FIXME: Channel weights when converting from more channels to fewer may need to be adjusted, see https://msdn.microsoft.com/en-us/library/windows/desktop/ff819070(v=vs.85).aspx
slouken@11641
    26
*/
slouken@11641
    27
icculus@11319
    28
#include "SDL.h"
slouken@0
    29
#include "SDL_audio.h"
icculus@1982
    30
#include "SDL_audio_c.h"
slouken@0
    31
slouken@10773
    32
#include "SDL_loadso.h"
icculus@6281
    33
#include "SDL_assert.h"
icculus@10757
    34
#include "../SDL_dataqueue.h"
icculus@10835
    35
#include "SDL_cpuinfo.h"
slouken@2716
    36
icculus@11583
    37
#define DEBUG_AUDIOSTREAM 0
icculus@11583
    38
icculus@10835
    39
#ifdef __SSE3__
icculus@10835
    40
#define HAVE_SSE3_INTRINSICS 1
icculus@10832
    41
#endif
icculus@10832
    42
icculus@10832
    43
#if HAVE_SSE3_INTRINSICS
icculus@11405
    44
/* Convert from stereo to mono. Average left and right. */
icculus@10832
    45
static void SDLCALL
icculus@10832
    46
SDL_ConvertStereoToMono_SSE3(SDL_AudioCVT * cvt, SDL_AudioFormat format)
icculus@10832
    47
{
icculus@10832
    48
    float *dst = (float *) cvt->buf;
icculus@10832
    49
    const float *src = dst;
icculus@10832
    50
    int i = cvt->len_cvt / 8;
icculus@10832
    51
icculus@10832
    52
    LOG_DEBUG_CONVERT("stereo", "mono (using SSE3)");
icculus@10832
    53
    SDL_assert(format == AUDIO_F32SYS);
icculus@10832
    54
icculus@10832
    55
    /* We can only do this if dst is aligned to 16 bytes; since src is the
icculus@10832
    56
       same pointer and it moves by 2, it can't be forcibly aligned. */
icculus@10832
    57
    if ((((size_t) dst) & 15) == 0) {
icculus@10832
    58
        /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
icculus@10832
    59
        const __m128 divby2 = _mm_set1_ps(0.5f);
icculus@10832
    60
        while (i >= 4) {   /* 4 * float32 */
icculus@10832
    61
            _mm_store_ps(dst, _mm_mul_ps(_mm_hadd_ps(_mm_load_ps(src), _mm_load_ps(src+4)), divby2));
icculus@10832
    62
            i -= 4; src += 8; dst += 4;
icculus@10832
    63
        }
icculus@10832
    64
    }
icculus@10832
    65
icculus@10832
    66
    /* Finish off any leftovers with scalar operations. */
icculus@10832
    67
    while (i) {
icculus@10832
    68
        *dst = (src[0] + src[1]) * 0.5f;
icculus@10832
    69
        dst++; i--; src += 2;
icculus@10832
    70
    }
icculus@10832
    71
icculus@10832
    72
    cvt->len_cvt /= 2;
icculus@10832
    73
    if (cvt->filters[++cvt->filter_index]) {
icculus@10832
    74
        cvt->filters[cvt->filter_index] (cvt, format);
icculus@10832
    75
    }
icculus@10832
    76
}
icculus@10832
    77
#endif
icculus@10832
    78
icculus@11405
    79
/* Convert from stereo to mono. Average left and right. */
icculus@1982
    80
static void SDLCALL
icculus@10793
    81
SDL_ConvertStereoToMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@0
    82
{
icculus@10575
    83
    float *dst = (float *) cvt->buf;
icculus@10575
    84
    const float *src = dst;
slouken@1895
    85
    int i;
slouken@0
    86
icculus@10575
    87
    LOG_DEBUG_CONVERT("stereo", "mono");
icculus@10575
    88
    SDL_assert(format == AUDIO_F32SYS);
icculus@1982
    89
icculus@10575
    90
    for (i = cvt->len_cvt / 8; i; --i, src += 2) {
icculus@10831
    91
        *(dst++) = (src[0] + src[1]) * 0.5f;
slouken@1895
    92
    }
icculus@1982
    93
slouken@1895
    94
    cvt->len_cvt /= 2;
slouken@1895
    95
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
    96
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
    97
    }
slouken@0
    98
}
slouken@0
    99
icculus@1982
   100
icculus@11405
   101
/* Convert from 5.1 to stereo. Average left and right, distribute center, discard LFE. */
icculus@1982
   102
static void SDLCALL
icculus@10793
   103
SDL_Convert51ToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   104
{
icculus@10575
   105
    float *dst = (float *) cvt->buf;
icculus@10575
   106
    const float *src = dst;
slouken@1895
   107
    int i;
slouken@942
   108
icculus@10793
   109
    LOG_DEBUG_CONVERT("5.1", "stereo");
icculus@10575
   110
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   111
icculus@11405
   112
    /* SDL's 5.1 layout: FL+FR+FC+LFE+BL+BR */
icculus@10575
   113
    for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
icculus@11405
   114
        const float front_center_distributed = src[2] * 0.5f;
icculus@11405
   115
        dst[0] = (src[0] + front_center_distributed + src[4]) / 2.5f;  /* left */
icculus@11405
   116
        dst[1] = (src[1] + front_center_distributed + src[5]) / 2.5f;  /* right */
icculus@1982
   117
    }
slouken@942
   118
slouken@1895
   119
    cvt->len_cvt /= 3;
slouken@1895
   120
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   121
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   122
    }
slouken@942
   123
}
slouken@942
   124
slouken@942
   125
icculus@11405
   126
/* Convert from quad to stereo. Average left and right. */
icculus@11405
   127
static void SDLCALL
icculus@11405
   128
SDL_ConvertQuadToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
icculus@11405
   129
{
icculus@11405
   130
    float *dst = (float *) cvt->buf;
icculus@11405
   131
    const float *src = dst;
icculus@11405
   132
    int i;
icculus@11405
   133
icculus@11405
   134
    LOG_DEBUG_CONVERT("quad", "stereo");
icculus@11405
   135
    SDL_assert(format == AUDIO_F32SYS);
icculus@11405
   136
icculus@11405
   137
    for (i = cvt->len_cvt / (sizeof (float) * 4); i; --i, src += 4, dst += 2) {
icculus@11405
   138
        dst[0] = (src[0] + src[2]) * 0.5f; /* left */
icculus@11405
   139
        dst[1] = (src[1] + src[3]) * 0.5f; /* right */
icculus@11405
   140
    }
icculus@11405
   141
slouken@11642
   142
    cvt->len_cvt /= 2;
icculus@11405
   143
    if (cvt->filters[++cvt->filter_index]) {
icculus@11405
   144
        cvt->filters[cvt->filter_index] (cvt, format);
icculus@11405
   145
    }
icculus@11405
   146
}
icculus@11405
   147
icculus@11405
   148
icculus@11405
   149
/* Convert from 7.1 to 5.1. Distribute sides across front and back. */
icculus@11405
   150
static void SDLCALL
icculus@11405
   151
SDL_Convert71To51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
icculus@11405
   152
{
icculus@11405
   153
    float *dst = (float *) cvt->buf;
icculus@11405
   154
    const float *src = dst;
icculus@11405
   155
    int i;
icculus@11405
   156
icculus@11405
   157
    LOG_DEBUG_CONVERT("7.1", "5.1");
icculus@11405
   158
    SDL_assert(format == AUDIO_F32SYS);
icculus@11405
   159
icculus@11405
   160
    for (i = cvt->len_cvt / (sizeof (float) * 8); i; --i, src += 8, dst += 6) {
icculus@11405
   161
        const float surround_left_distributed = src[6] * 0.5f;
icculus@11405
   162
        const float surround_right_distributed = src[7] * 0.5f;
icculus@11405
   163
        dst[0] = (src[0] + surround_left_distributed) / 1.5f;  /* FL */
icculus@11405
   164
        dst[1] = (src[1] + surround_right_distributed) / 1.5f;  /* FR */
icculus@11405
   165
        dst[2] = src[2] / 1.5f; /* CC */
icculus@11405
   166
        dst[3] = src[3] / 1.5f; /* LFE */
icculus@11405
   167
        dst[4] = (src[4] + surround_left_distributed) / 1.5f;  /* BL */
icculus@11405
   168
        dst[5] = (src[5] + surround_right_distributed) / 1.5f;  /* BR */
icculus@11405
   169
    }
icculus@11405
   170
icculus@11405
   171
    cvt->len_cvt /= 8;
icculus@11405
   172
    cvt->len_cvt *= 6;
icculus@11405
   173
    if (cvt->filters[++cvt->filter_index]) {
icculus@11405
   174
        cvt->filters[cvt->filter_index] (cvt, format);
icculus@11405
   175
    }
icculus@11405
   176
}
icculus@11405
   177
icculus@11405
   178
icculus@11405
   179
/* Convert from 5.1 to quad. Distribute center across front, discard LFE. */
icculus@1982
   180
static void SDLCALL
icculus@10793
   181
SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   182
{
icculus@10575
   183
    float *dst = (float *) cvt->buf;
icculus@10575
   184
    const float *src = dst;
slouken@1895
   185
    int i;
slouken@942
   186
icculus@10793
   187
    LOG_DEBUG_CONVERT("5.1", "quad");
icculus@10575
   188
    SDL_assert(format == AUDIO_F32SYS);
slouken@1895
   189
icculus@11405
   190
    /* SDL's 4.0 layout: FL+FR+BL+BR */
icculus@11405
   191
    /* SDL's 5.1 layout: FL+FR+FC+LFE+BL+BR */
icculus@10575
   192
    for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
icculus@11405
   193
        const float front_center_distributed = src[2] * 0.5f;
icculus@11405
   194
        dst[0] = (src[0] + front_center_distributed) / 1.5f;  /* FL */
icculus@11405
   195
        dst[1] = (src[1] + front_center_distributed) / 1.5f;  /* FR */
icculus@11405
   196
        dst[2] = src[4] / 1.5f;  /* BL */
icculus@11405
   197
        dst[3] = src[5] / 1.5f;  /* BR */
icculus@1982
   198
    }
slouken@942
   199
icculus@1982
   200
    cvt->len_cvt /= 6;
icculus@1982
   201
    cvt->len_cvt *= 4;
slouken@1895
   202
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   203
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   204
    }
slouken@942
   205
}
slouken@0
   206
icculus@10793
   207
icculus@11405
   208
/* Upmix mono to stereo (by duplication) */
icculus@1982
   209
static void SDLCALL
icculus@10793
   210
SDL_ConvertMonoToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@0
   211
{
icculus@10575
   212
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   213
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
slouken@1895
   214
    int i;
slouken@0
   215
icculus@10575
   216
    LOG_DEBUG_CONVERT("mono", "stereo");
icculus@10575
   217
    SDL_assert(format == AUDIO_F32SYS);
slouken@0
   218
icculus@10575
   219
    for (i = cvt->len_cvt / sizeof (float); i; --i) {
icculus@10575
   220
        src--;
icculus@10575
   221
        dst -= 2;
icculus@10575
   222
        dst[0] = dst[1] = *src;
icculus@1982
   223
    }
slouken@0
   224
slouken@1895
   225
    cvt->len_cvt *= 2;
slouken@1895
   226
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   227
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   228
    }
slouken@0
   229
}
slouken@0
   230
slouken@942
   231
icculus@11405
   232
/* Upmix stereo to a pseudo-5.1 stream */
icculus@1982
   233
static void SDLCALL
icculus@10793
   234
SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   235
{
slouken@1895
   236
    int i;
icculus@10575
   237
    float lf, rf, ce;
icculus@10575
   238
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   239
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 3);
slouken@942
   240
icculus@10575
   241
    LOG_DEBUG_CONVERT("stereo", "5.1");
icculus@10575
   242
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   243
icculus@11405
   244
    for (i = cvt->len_cvt / (sizeof(float) * 2); i; --i) {
icculus@10575
   245
        dst -= 6;
icculus@10575
   246
        src -= 2;
icculus@10575
   247
        lf = src[0];
icculus@10575
   248
        rf = src[1];
icculus@10793
   249
        ce = (lf + rf) * 0.5f;
icculus@11405
   250
        /* !!! FIXME: FL and FR may clip */
icculus@10793
   251
        dst[0] = lf + (lf - ce);  /* FL */
icculus@10793
   252
        dst[1] = rf + (rf - ce);  /* FR */
icculus@10793
   253
        dst[2] = ce;  /* FC */
icculus@11405
   254
        dst[3] = 0;   /* LFE (only meant for special LFE effects) */
icculus@10793
   255
        dst[4] = lf;  /* BL */
icculus@10793
   256
        dst[5] = rf;  /* BR */
icculus@10575
   257
    }
icculus@1982
   258
slouken@1895
   259
    cvt->len_cvt *= 3;
slouken@1895
   260
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   261
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   262
    }
slouken@942
   263
}
slouken@942
   264
slouken@942
   265
icculus@11405
   266
/* Upmix quad to a pseudo-5.1 stream */
icculus@11405
   267
static void SDLCALL
icculus@11405
   268
SDL_ConvertQuadTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
icculus@11405
   269
{
icculus@11405
   270
    int i;
icculus@11405
   271
    float lf, rf, lb, rb, ce;
icculus@11405
   272
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@11405
   273
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 3 / 2);
icculus@11405
   274
icculus@11405
   275
    LOG_DEBUG_CONVERT("quad", "5.1");
icculus@11405
   276
    SDL_assert(format == AUDIO_F32SYS);
icculus@11405
   277
    SDL_assert(cvt->len_cvt % (sizeof(float) * 4) == 0);
icculus@11405
   278
icculus@11405
   279
    for (i = cvt->len_cvt / (sizeof(float) * 4); i; --i) {
icculus@11405
   280
        dst -= 6;
icculus@11405
   281
        src -= 4;
icculus@11405
   282
        lf = src[0];
icculus@11405
   283
        rf = src[1];
icculus@11405
   284
        lb = src[2];
icculus@11405
   285
        rb = src[3];
icculus@11405
   286
        ce = (lf + rf) * 0.5f;
icculus@11405
   287
        /* !!! FIXME: FL and FR may clip */
icculus@11405
   288
        dst[0] = lf + (lf - ce);  /* FL */
icculus@11405
   289
        dst[1] = rf + (rf - ce);  /* FR */
icculus@11405
   290
        dst[2] = ce;  /* FC */
icculus@11405
   291
        dst[3] = 0;   /* LFE (only meant for special LFE effects) */
icculus@11405
   292
        dst[4] = lb;  /* BL */
icculus@11405
   293
        dst[5] = rb;  /* BR */
icculus@11405
   294
    }
icculus@11405
   295
icculus@11405
   296
    cvt->len_cvt = cvt->len_cvt * 3 / 2;
icculus@11405
   297
    if (cvt->filters[++cvt->filter_index]) {
icculus@11405
   298
        cvt->filters[cvt->filter_index] (cvt, format);
icculus@11405
   299
    }
icculus@11405
   300
}
icculus@11405
   301
icculus@11405
   302
icculus@11405
   303
/* Upmix stereo to a pseudo-4.0 stream (by duplication) */
icculus@1982
   304
static void SDLCALL
icculus@10793
   305
SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
slouken@942
   306
{
icculus@10575
   307
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@10575
   308
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
icculus@10793
   309
    float lf, rf;
slouken@1895
   310
    int i;
slouken@942
   311
icculus@10575
   312
    LOG_DEBUG_CONVERT("stereo", "quad");
icculus@10575
   313
    SDL_assert(format == AUDIO_F32SYS);
slouken@942
   314
icculus@11405
   315
    for (i = cvt->len_cvt / (sizeof(float) * 2); i; --i) {
icculus@10575
   316
        dst -= 4;
icculus@10575
   317
        src -= 2;
icculus@10575
   318
        lf = src[0];
icculus@10575
   319
        rf = src[1];
icculus@10793
   320
        dst[0] = lf;  /* FL */
icculus@10793
   321
        dst[1] = rf;  /* FR */
icculus@10793
   322
        dst[2] = lf;  /* BL */
icculus@10793
   323
        dst[3] = rf;  /* BR */
icculus@10575
   324
    }
slouken@0
   325
slouken@1895
   326
    cvt->len_cvt *= 2;
slouken@1895
   327
    if (cvt->filters[++cvt->filter_index]) {
slouken@1895
   328
        cvt->filters[cvt->filter_index] (cvt, format);
slouken@1895
   329
    }
slouken@942
   330
}
slouken@942
   331
icculus@11405
   332
icculus@11405
   333
/* Upmix 5.1 to 7.1 */
icculus@11405
   334
static void SDLCALL
icculus@11405
   335
SDL_Convert51To71(SDL_AudioCVT * cvt, SDL_AudioFormat format)
icculus@11405
   336
{
icculus@11405
   337
    float lf, rf, lb, rb, ls, rs;
icculus@11405
   338
    int i;
icculus@11405
   339
    const float *src = (const float *) (cvt->buf + cvt->len_cvt);
icculus@11405
   340
    float *dst = (float *) (cvt->buf + cvt->len_cvt * 4 / 3);
icculus@11405
   341
icculus@11405
   342
    LOG_DEBUG_CONVERT("5.1", "7.1");
icculus@11405
   343
    SDL_assert(format == AUDIO_F32SYS);
icculus@11405
   344
    SDL_assert(cvt->len_cvt % (sizeof(float) * 6) == 0);
icculus@11405
   345
icculus@11405
   346
    for (i = cvt->len_cvt / (sizeof(float) * 6); i; --i) {
icculus@11405
   347
        dst -= 8;
icculus@11405
   348
        src -= 6;
icculus@11405
   349
        lf = src[0];
icculus@11405
   350
        rf = src[1];
icculus@11405
   351
        lb = src[4];
icculus@11405
   352
        rb = src[5];
icculus@11405
   353
        ls = (lf + lb) * 0.5f;
icculus@11405
   354
        rs = (rf + rb) * 0.5f;
icculus@11405
   355
        /* !!! FIXME: these four may clip */
icculus@11405
   356
        lf += lf - ls;
icculus@11405
   357
        rf += rf - ls;
icculus@11405
   358
        lb += lb - ls;
icculus@11405
   359
        rb += rb - ls;
icculus@11405
   360
        dst[3] = src[3];  /* LFE */
icculus@11405
   361
        dst[2] = src[2];  /* FC */
icculus@11405
   362
        dst[7] = rs; /* SR */
icculus@11405
   363
        dst[6] = ls; /* SL */
icculus@11405
   364
        dst[5] = rb;  /* BR */
icculus@11405
   365
        dst[4] = lb;  /* BL */
icculus@11405
   366
        dst[1] = rf;  /* FR */
icculus@11405
   367
        dst[0] = lf;  /* FL */
icculus@11405
   368
    }
icculus@11405
   369
icculus@11405
   370
    cvt->len_cvt = cvt->len_cvt * 4 / 3;
icculus@11405
   371
icculus@11405
   372
    if (cvt->filters[++cvt->filter_index]) {
icculus@11405
   373
        cvt->filters[cvt->filter_index] (cvt, format);
icculus@11405
   374
    }
icculus@11405
   375
}
icculus@11405
   376
icculus@11508
   377
/* SDL's resampler uses a "bandlimited interpolation" algorithm:
icculus@11508
   378
     https://ccrma.stanford.edu/~jos/resample/ */
icculus@11508
   379
icculus@11508
   380
#define RESAMPLER_ZERO_CROSSINGS 5
icculus@11508
   381
#define RESAMPLER_BITS_PER_SAMPLE 16
icculus@11508
   382
#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING  (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1))
icculus@11508
   383
#define RESAMPLER_FILTER_SIZE ((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) + 1)
icculus@11508
   384
icculus@11508
   385
/* This is a "modified" bessel function, so you can't use POSIX j0() */
icculus@11508
   386
static double
icculus@11508
   387
bessel(const double x)
icculus@11508
   388
{
icculus@11508
   389
    const double xdiv2 = x / 2.0;
icculus@11508
   390
    double i0 = 1.0f;
icculus@11508
   391
    double f = 1.0f;
icculus@11508
   392
    int i = 1;
icculus@11508
   393
icculus@11508
   394
    while (SDL_TRUE) {
icculus@11508
   395
        const double diff = SDL_pow(xdiv2, i * 2) / SDL_pow(f, 2);
icculus@11508
   396
        if (diff < 1.0e-21f) {
icculus@11508
   397
            break;
icculus@11508
   398
        }
icculus@11508
   399
        i0 += diff;
icculus@11508
   400
        i++;
icculus@11508
   401
        f *= (double) i;
icculus@11508
   402
    }
icculus@11508
   403
icculus@11508
   404
    return i0;
icculus@11508
   405
}
icculus@11508
   406
icculus@11508
   407
/* build kaiser table with cardinal sine applied to it, and array of differences between elements. */
icculus@11508
   408
static void
icculus@11508
   409
kaiser_and_sinc(float *table, float *diffs, const int tablelen, const double beta)
icculus@11508
   410
{
icculus@11508
   411
    const int lenm1 = tablelen - 1;
icculus@11508
   412
    const int lenm1div2 = lenm1 / 2;
icculus@11508
   413
    int i;
icculus@11508
   414
icculus@11508
   415
    table[0] = 1.0f;
icculus@11508
   416
    for (i = 1; i < tablelen; i++) {
icculus@11508
   417
        const double kaiser = bessel(beta * SDL_sqrt(1.0 - SDL_pow(((i - lenm1) / 2.0) / lenm1div2, 2.0))) / bessel(beta);
icculus@11508
   418
        table[tablelen - i] = (float) kaiser;
icculus@11508
   419
    }
icculus@11508
   420
icculus@11508
   421
    for (i = 1; i < tablelen; i++) {
icculus@11508
   422
        const float x = (((float) i) / ((float) RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) * ((float) M_PI);
icculus@11508
   423
        table[i] *= SDL_sinf(x) / x;
icculus@11508
   424
        diffs[i - 1] = table[i] - table[i - 1];
icculus@11508
   425
    }
icculus@11508
   426
    diffs[lenm1] = 0.0f;
icculus@11508
   427
}
icculus@11508
   428
icculus@11508
   429
icculus@11508
   430
static SDL_SpinLock ResampleFilterSpinlock = 0;
icculus@11508
   431
static float *ResamplerFilter = NULL;
icculus@11508
   432
static float *ResamplerFilterDifference = NULL;
icculus@11508
   433
icculus@11508
   434
int
icculus@11508
   435
SDL_PrepareResampleFilter(void)
icculus@11508
   436
{
icculus@11508
   437
    SDL_AtomicLock(&ResampleFilterSpinlock);
icculus@11508
   438
    if (!ResamplerFilter) {
icculus@11508
   439
        /* if dB > 50, beta=(0.1102 * (dB - 8.7)), according to Matlab. */
icculus@11508
   440
        const double dB = 80.0;
icculus@11508
   441
        const double beta = 0.1102 * (dB - 8.7);
icculus@11508
   442
        const size_t alloclen = RESAMPLER_FILTER_SIZE * sizeof (float);
icculus@11508
   443
icculus@11508
   444
        ResamplerFilter = (float *) SDL_malloc(alloclen);
icculus@11508
   445
        if (!ResamplerFilter) {
icculus@11508
   446
            SDL_AtomicUnlock(&ResampleFilterSpinlock);
icculus@11508
   447
            return SDL_OutOfMemory();
icculus@11508
   448
        }
icculus@11508
   449
icculus@11508
   450
        ResamplerFilterDifference = (float *) SDL_malloc(alloclen);
icculus@11508
   451
        if (!ResamplerFilterDifference) {
icculus@11508
   452
            SDL_free(ResamplerFilter);
icculus@11508
   453
            ResamplerFilter = NULL;
icculus@11508
   454
            SDL_AtomicUnlock(&ResampleFilterSpinlock);
icculus@11508
   455
            return SDL_OutOfMemory();
icculus@11508
   456
        }
icculus@11508
   457
        kaiser_and_sinc(ResamplerFilter, ResamplerFilterDifference, RESAMPLER_FILTER_SIZE, beta);
icculus@11508
   458
    }
icculus@11508
   459
    SDL_AtomicUnlock(&ResampleFilterSpinlock);
icculus@11508
   460
    return 0;
icculus@11508
   461
}
icculus@11508
   462
icculus@11508
   463
void
icculus@11508
   464
SDL_FreeResampleFilter(void)
icculus@11508
   465
{
icculus@11508
   466
    SDL_free(ResamplerFilter);
icculus@11508
   467
    SDL_free(ResamplerFilterDifference);
icculus@11508
   468
    ResamplerFilter = NULL;
icculus@11508
   469
    ResamplerFilterDifference = NULL;
icculus@11508
   470
}
icculus@11508
   471
icculus@11517
   472
static int
icculus@11517
   473
ResamplerPadding(const int inrate, const int outrate)
icculus@11517
   474
{
icculus@11583
   475
    if (inrate == outrate) {
icculus@11583
   476
        return 0;
icculus@11583
   477
    } else if (inrate > outrate) {
icculus@11583
   478
        return (int) SDL_ceil(((float) (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * inrate) / ((float) outrate)));
icculus@11583
   479
    }
icculus@11583
   480
    return RESAMPLER_SAMPLES_PER_ZERO_CROSSING;
icculus@11517
   481
}
icculus@11405
   482
icculus@11517
   483
/* lpadding and rpadding are expected to be buffers of (ResamplePadding(inrate, outrate) * chans * sizeof (float)) bytes. */
icculus@10799
   484
static int
icculus@11508
   485
SDL_ResampleAudio(const int chans, const int inrate, const int outrate,
icculus@11583
   486
                        const float *lpadding, const float *rpadding,
icculus@11583
   487
                        const float *inbuf, const int inbuflen,
icculus@11583
   488
                        float *outbuf, const int outbuflen)
icculus@10799
   489
{
icculus@11592
   490
    const double finrate = (double) inrate;
icculus@11595
   491
    const double outtimeincr = 1.0 / ((float) outrate);
icculus@11595
   492
    const double  ratio = ((float) outrate) / ((float) inrate);
icculus@11517
   493
    const int paddinglen = ResamplerPadding(inrate, outrate);
icculus@10817
   494
    const int framelen = chans * (int)sizeof (float);
icculus@11508
   495
    const int inframes = inbuflen / framelen;
icculus@11508
   496
    const int wantedoutframes = (int) ((inbuflen / framelen) * ratio);  /* outbuflen isn't total to write, it's total available. */
icculus@11508
   497
    const int maxoutframes = outbuflen / framelen;
icculus@11583
   498
    const int outframes = SDL_min(wantedoutframes, maxoutframes);
icculus@11508
   499
    float *dst = outbuf;
icculus@11595
   500
    double outtime = 0.0;
icculus@11508
   501
    int i, j, chan;
icculus@10799
   502
icculus@11508
   503
    for (i = 0; i < outframes; i++) {
icculus@11508
   504
        const int srcindex = (int) (outtime * inrate);
icculus@11595
   505
        const double intime = ((double) srcindex) / finrate;
icculus@11595
   506
        const double innexttime = ((double) (srcindex + 1)) / finrate;
icculus@11595
   507
        const double interpolation1 = 1.0 - ((innexttime - outtime) / (innexttime - intime));
icculus@11508
   508
        const int filterindex1 = (int) (interpolation1 * RESAMPLER_SAMPLES_PER_ZERO_CROSSING);
icculus@11595
   509
        const double interpolation2 = 1.0 - interpolation1;
icculus@11541
   510
        const int filterindex2 = (int) (interpolation2 * RESAMPLER_SAMPLES_PER_ZERO_CROSSING);
icculus@10833
   511
icculus@11508
   512
        for (chan = 0; chan < chans; chan++) {
icculus@11508
   513
            float outsample = 0.0f;
icculus@11508
   514
icculus@11508
   515
            /* do this twice to calculate the sample, once for the "left wing" and then same for the right. */
icculus@11508
   516
            /* !!! FIXME: do both wings in one loop */
icculus@11508
   517
            for (j = 0; (filterindex1 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) < RESAMPLER_FILTER_SIZE; j++) {
icculus@11508
   518
                const int srcframe = srcindex - j;
icculus@11517
   519
                /* !!! FIXME: we can bubble this conditional out of here by doing a pre loop. */
icculus@11517
   520
                const float insample = (srcframe < 0) ? lpadding[((paddinglen + srcframe) * chans) + chan] : inbuf[(srcframe * chans) + chan];
slouken@11611
   521
                outsample += (float)(insample * (ResamplerFilter[filterindex1 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)] + (interpolation1 * ResamplerFilterDifference[filterindex1 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)])));
icculus@10840
   522
            }
icculus@11508
   523
icculus@11508
   524
            for (j = 0; (filterindex2 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) < RESAMPLER_FILTER_SIZE; j++) {
icculus@11508
   525
                const int srcframe = srcindex + 1 + j;
icculus@11517
   526
                /* !!! FIXME: we can bubble this conditional out of here by doing a post loop. */
icculus@11517
   527
                const float insample = (srcframe >= inframes) ? rpadding[((srcframe - inframes) * chans) + chan] : inbuf[(srcframe * chans) + chan];
slouken@11611
   528
                outsample += (float)(insample * (ResamplerFilter[filterindex2 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)] + (interpolation2 * ResamplerFilterDifference[filterindex2 + (j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING)])));
icculus@10840
   529
            }
icculus@11508
   530
            *(dst++) = outsample;
icculus@10799
   531
        }
icculus@10833
   532
icculus@11596
   533
        outtime += outtimeincr;
icculus@10799
   534
    }
icculus@10799
   535
icculus@11508
   536
    return outframes * chans * sizeof (float);
icculus@10799
   537
}
icculus@10799
   538
slouken@1895
   539
int
slouken@1895
   540
SDL_ConvertAudio(SDL_AudioCVT * cvt)
slouken@0
   541
{
icculus@3021
   542
    /* !!! FIXME: (cvt) should be const; stack-copy it here. */
icculus@3021
   543
    /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
icculus@3021
   544
slouken@1895
   545
    /* Make sure there's data to convert */
slouken@1895
   546
    if (cvt->buf == NULL) {
icculus@10575
   547
        return SDL_SetError("No buffer allocated for conversion");
slouken@1895
   548
    }
icculus@10575
   549
slouken@1895
   550
    /* Return okay if no conversion is necessary */
slouken@1895
   551
    cvt->len_cvt = cvt->len;
slouken@1895
   552
    if (cvt->filters[0] == NULL) {
icculus@10575
   553
        return 0;
slouken@1895
   554
    }
slouken@0
   555
slouken@1895
   556
    /* Set up the conversion and go! */
slouken@1895
   557
    cvt->filter_index = 0;
slouken@1895
   558
    cvt->filters[0] (cvt, cvt->src_format);
icculus@10575
   559
    return 0;
slouken@0
   560
}
slouken@0
   561
icculus@10575
   562
static void SDLCALL
icculus@10575
   563
SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format)
icculus@1982
   564
{
slouken@10579
   565
#if DEBUG_CONVERT
slouken@10579
   566
    printf("Converting byte order\n");
slouken@10579
   567
#endif
icculus@10575
   568
icculus@10575
   569
    switch (SDL_AUDIO_BITSIZE(format)) {
icculus@10575
   570
        #define CASESWAP(b) \
icculus@10575
   571
            case b: { \
icculus@10575
   572
                Uint##b *ptr = (Uint##b *) cvt->buf; \
icculus@10575
   573
                int i; \
icculus@10575
   574
                for (i = cvt->len_cvt / sizeof (*ptr); i; --i, ++ptr) { \
icculus@10575
   575
                    *ptr = SDL_Swap##b(*ptr); \
icculus@10575
   576
                } \
icculus@10575
   577
                break; \
icculus@10575
   578
            }
icculus@1982
   579
icculus@10575
   580
        CASESWAP(16);
icculus@10575
   581
        CASESWAP(32);
icculus@10575
   582
        CASESWAP(64);
icculus@10575
   583
icculus@10575
   584
        #undef CASESWAP
icculus@10575
   585
icculus@10575
   586
        default: SDL_assert(!"unhandled byteswap datatype!"); break;
icculus@10575
   587
    }
icculus@10575
   588
icculus@10575
   589
    if (cvt->filters[++cvt->filter_index]) {
icculus@10575
   590
        /* flip endian flag for data. */
icculus@10575
   591
        if (format & SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   592
            format &= ~SDL_AUDIO_MASK_ENDIAN;
icculus@10575
   593
        } else {
icculus@10575
   594
            format |= SDL_AUDIO_MASK_ENDIAN;
icculus@10575
   595
        }
icculus@10575
   596
        cvt->filters[cvt->filter_index](cvt, format);
icculus@10575
   597
    }
icculus@1982
   598
}
icculus@1982
   599
slouken@11096
   600
static int
slouken@11096
   601
SDL_AddAudioCVTFilter(SDL_AudioCVT *cvt, const SDL_AudioFilter filter)
slouken@11096
   602
{
slouken@11096
   603
    if (cvt->filter_index >= SDL_AUDIOCVT_MAX_FILTERS) {
slouken@11096
   604
        return SDL_SetError("Too many filters needed for conversion, exceeded maximum of %d", SDL_AUDIOCVT_MAX_FILTERS);
slouken@11096
   605
    }
slouken@11096
   606
    if (filter == NULL) {
slouken@11096
   607
        return SDL_SetError("Audio filter pointer is NULL");
slouken@11096
   608
    }
slouken@11096
   609
    cvt->filters[cvt->filter_index++] = filter;
slouken@11096
   610
    cvt->filters[cvt->filter_index] = NULL; /* Moving terminator */
slouken@11096
   611
    return 0;
slouken@11096
   612
}
icculus@1982
   613
icculus@1982
   614
static int
icculus@10575
   615
SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt)
icculus@1982
   616
{
icculus@10575
   617
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@1982
   618
icculus@10575
   619
    if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
slouken@11096
   620
        if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) {
slouken@11096
   621
            return -1;
slouken@11096
   622
        }
icculus@10575
   623
        retval = 1;  /* added a converter. */
icculus@10575
   624
    }
icculus@1982
   625
icculus@10575
   626
    if (!SDL_AUDIO_ISFLOAT(src_fmt)) {
icculus@10576
   627
        const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt);
icculus@10576
   628
        const Uint16 dst_bitsize = 32;
icculus@10575
   629
        SDL_AudioFilter filter = NULL;
icculus@10576
   630
icculus@10575
   631
        switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   632
            case AUDIO_S8: filter = SDL_Convert_S8_to_F32; break;
icculus@10575
   633
            case AUDIO_U8: filter = SDL_Convert_U8_to_F32; break;
icculus@10575
   634
            case AUDIO_S16: filter = SDL_Convert_S16_to_F32; break;
philipp@10591
   635
            case AUDIO_U16: filter = SDL_Convert_U16_to_F32; break;
icculus@10575
   636
            case AUDIO_S32: filter = SDL_Convert_S32_to_F32; break;
icculus@10575
   637
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@1982
   638
        }
icculus@1982
   639
icculus@10575
   640
        if (!filter) {
icculus@11319
   641
            return SDL_SetError("No conversion from source format to float available");
icculus@10575
   642
        }
icculus@10575
   643
slouken@11096
   644
        if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
slouken@11096
   645
            return -1;
slouken@11096
   646
        }
icculus@1982
   647
        if (src_bitsize < dst_bitsize) {
icculus@1982
   648
            const int mult = (dst_bitsize / src_bitsize);
icculus@1982
   649
            cvt->len_mult *= mult;
icculus@1982
   650
            cvt->len_ratio *= mult;
icculus@1982
   651
        } else if (src_bitsize > dst_bitsize) {
icculus@1982
   652
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@1982
   653
        }
icculus@10576
   654
icculus@10575
   655
        retval = 1;  /* added a converter. */
icculus@1982
   656
    }
icculus@1982
   657
icculus@10575
   658
    return retval;
icculus@1982
   659
}
icculus@1982
   660
icculus@10575
   661
static int
icculus@10575
   662
SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
slouken@2716
   663
{
icculus@10575
   664
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@10575
   665
icculus@10575
   666
    if (!SDL_AUDIO_ISFLOAT(dst_fmt)) {
icculus@10577
   667
        const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt);
icculus@10577
   668
        const Uint16 src_bitsize = 32;
icculus@10575
   669
        SDL_AudioFilter filter = NULL;
icculus@10575
   670
        switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) {
icculus@10575
   671
            case AUDIO_S8: filter = SDL_Convert_F32_to_S8; break;
icculus@10575
   672
            case AUDIO_U8: filter = SDL_Convert_F32_to_U8; break;
icculus@10575
   673
            case AUDIO_S16: filter = SDL_Convert_F32_to_S16; break;
philipp@10591
   674
            case AUDIO_U16: filter = SDL_Convert_F32_to_U16; break;
icculus@10575
   675
            case AUDIO_S32: filter = SDL_Convert_F32_to_S32; break;
icculus@10575
   676
            default: SDL_assert(!"Unexpected audio format!"); break;
icculus@10575
   677
        }
icculus@10575
   678
icculus@10575
   679
        if (!filter) {
icculus@11319
   680
            return SDL_SetError("No conversion from float to destination format available");
icculus@10575
   681
        }
slouken@2716
   682
slouken@11096
   683
        if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
slouken@11096
   684
            return -1;
slouken@11096
   685
        }
icculus@10575
   686
        if (src_bitsize < dst_bitsize) {
icculus@10575
   687
            const int mult = (dst_bitsize / src_bitsize);
icculus@10575
   688
            cvt->len_mult *= mult;
icculus@10575
   689
            cvt->len_ratio *= mult;
icculus@10575
   690
        } else if (src_bitsize > dst_bitsize) {
icculus@10575
   691
            cvt->len_ratio /= (src_bitsize / dst_bitsize);
icculus@10575
   692
        }
icculus@10575
   693
        retval = 1;  /* added a converter. */
icculus@10575
   694
    }
icculus@10575
   695
icculus@10575
   696
    if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) {
slouken@11096
   697
        if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) {
slouken@11096
   698
            return -1;
slouken@11096
   699
        }
icculus@10575
   700
        retval = 1;  /* added a converter. */
icculus@10575
   701
    }
icculus@10575
   702
icculus@10575
   703
    return retval;
slouken@2716
   704
}
slouken@2716
   705
icculus@10799
   706
static void
icculus@10799
   707
SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format)
icculus@10799
   708
{
icculus@11508
   709
    /* !!! FIXME in 2.1: there are ten slots in the filter list, and the theoretical maximum we use is six (seven with NULL terminator).
icculus@11508
   710
       !!! FIXME in 2.1:   We need to store data for this resampler, because the cvt structure doesn't store the original sample rates,
icculus@11508
   711
       !!! FIXME in 2.1:   so we steal the ninth and tenth slot.  :( */
icculus@11517
   712
    const int inrate = (int) (size_t) cvt->filters[SDL_AUDIOCVT_MAX_FILTERS-1];
icculus@11517
   713
    const int outrate = (int) (size_t) cvt->filters[SDL_AUDIOCVT_MAX_FILTERS];
icculus@10799
   714
    const float *src = (const float *) cvt->buf;
icculus@10799
   715
    const int srclen = cvt->len_cvt;
icculus@11508
   716
    /*float *dst = (float *) cvt->buf;
icculus@11508
   717
    const int dstlen = (cvt->len * cvt->len_mult);*/
icculus@11508
   718
    /* !!! FIXME: remove this if we can get the resampler to work in-place again. */
icculus@11508
   719
    float *dst = (float *) (cvt->buf + srclen);
icculus@11508
   720
    const int dstlen = (cvt->len * cvt->len_mult) - srclen;
slouken@12803
   721
    const int requestedpadding = ResamplerPadding(inrate, outrate);
slouken@12803
   722
    int paddingsamples;
slouken@11519
   723
    float *padding;
icculus@10756
   724
slouken@12803
   725
    if (requestedpadding < INT32_MAX / chans) {
slouken@12803
   726
        paddingsamples = requestedpadding * chans;
slouken@12803
   727
    } else {
slouken@12803
   728
        paddingsamples = 0;
slouken@12803
   729
    }
icculus@10799
   730
    SDL_assert(format == AUDIO_F32SYS);
icculus@10799
   731
icculus@11517
   732
    /* we keep no streaming state here, so pad with silence on both ends. */
icculus@12036
   733
    padding = (float *) SDL_calloc(paddingsamples ? paddingsamples : 1, sizeof (float));
slouken@11519
   734
    if (!padding) {
slouken@11519
   735
        SDL_OutOfMemory();
slouken@11519
   736
        return;
slouken@11519
   737
    }
icculus@10799
   738
icculus@11517
   739
    cvt->len_cvt = SDL_ResampleAudio(chans, inrate, outrate, padding, padding, src, srclen, dst, dstlen);
icculus@11508
   740
icculus@11585
   741
    SDL_free(padding);
slouken@11519
   742
icculus@11586
   743
    SDL_memmove(cvt->buf, dst, cvt->len_cvt);  /* !!! FIXME: remove this if we can get the resampler to work in-place again. */
icculus@11508
   744
icculus@10799
   745
    if (cvt->filters[++cvt->filter_index]) {
icculus@10799
   746
        cvt->filters[cvt->filter_index](cvt, format);
icculus@10799
   747
    }
icculus@10799
   748
}
icculus@10799
   749
icculus@10799
   750
/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't
icculus@10799
   751
   !!! FIXME:  store channel info, so we have to have function entry
icculus@10799
   752
   !!! FIXME:  points for each supported channel count and multiple
icculus@10799
   753
   !!! FIXME:  vs arbitrary. When we rev the ABI, clean this up. */
icculus@10756
   754
#define RESAMPLER_FUNCS(chans) \
icculus@10756
   755
    static void SDLCALL \
icculus@10799
   756
    SDL_ResampleCVT_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10799
   757
        SDL_ResampleCVT(cvt, chans, format); \
icculus@10756
   758
    }
icculus@10756
   759
RESAMPLER_FUNCS(1)
icculus@10756
   760
RESAMPLER_FUNCS(2)
icculus@10756
   761
RESAMPLER_FUNCS(4)
icculus@10756
   762
RESAMPLER_FUNCS(6)
icculus@10756
   763
RESAMPLER_FUNCS(8)
icculus@10756
   764
#undef RESAMPLER_FUNCS
icculus@10756
   765
icculus@10799
   766
static SDL_AudioFilter
icculus@10799
   767
ChooseCVTResampler(const int dst_channels)
slouken@2716
   768
{
icculus@10799
   769
    switch (dst_channels) {
icculus@10799
   770
        case 1: return SDL_ResampleCVT_c1;
icculus@10799
   771
        case 2: return SDL_ResampleCVT_c2;
icculus@10799
   772
        case 4: return SDL_ResampleCVT_c4;
icculus@10799
   773
        case 6: return SDL_ResampleCVT_c6;
icculus@10799
   774
        case 8: return SDL_ResampleCVT_c8;
icculus@10799
   775
        default: break;
slouken@2716
   776
    }
slouken@2716
   777
icculus@10799
   778
    return NULL;
icculus@10756
   779
}
icculus@10575
   780
icculus@3021
   781
static int
icculus@10756
   782
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
icculus@10756
   783
                          const int src_rate, const int dst_rate)
icculus@3021
   784
{
icculus@10756
   785
    SDL_AudioFilter filter;
slouken@2716
   786
icculus@10756
   787
    if (src_rate == dst_rate) {
icculus@10756
   788
        return 0;  /* no conversion necessary. */
slouken@2716
   789
    }
slouken@2716
   790
icculus@10799
   791
    filter = ChooseCVTResampler(dst_channels);
icculus@10756
   792
    if (filter == NULL) {
icculus@10756
   793
        return SDL_SetError("No conversion available for these rates");
icculus@10756
   794
    }
icculus@10756
   795
icculus@11508
   796
    if (SDL_PrepareResampleFilter() < 0) {
icculus@11508
   797
        return -1;
icculus@11508
   798
    }
icculus@11508
   799
icculus@10756
   800
    /* Update (cvt) with filter details... */
slouken@11096
   801
    if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
slouken@11096
   802
        return -1;
slouken@11096
   803
    }
icculus@11508
   804
icculus@11508
   805
    /* !!! FIXME in 2.1: there are ten slots in the filter list, and the theoretical maximum we use is six (seven with NULL terminator).
icculus@11508
   806
       !!! FIXME in 2.1:   We need to store data for this resampler, because the cvt structure doesn't store the original sample rates,
icculus@11508
   807
       !!! FIXME in 2.1:   so we steal the ninth and tenth slot.  :( */
icculus@11508
   808
    if (cvt->filter_index >= (SDL_AUDIOCVT_MAX_FILTERS-2)) {
icculus@11508
   809
        return SDL_SetError("Too many filters needed for conversion, exceeded maximum of %d", SDL_AUDIOCVT_MAX_FILTERS-2);
icculus@11508
   810
    }
icculus@11508
   811
    cvt->filters[SDL_AUDIOCVT_MAX_FILTERS-1] = (SDL_AudioFilter) (size_t) src_rate;
icculus@11508
   812
    cvt->filters[SDL_AUDIOCVT_MAX_FILTERS] = (SDL_AudioFilter) (size_t) dst_rate;
icculus@11508
   813
icculus@10756
   814
    if (src_rate < dst_rate) {
icculus@10756
   815
        const double mult = ((double) dst_rate) / ((double) src_rate);
icculus@10756
   816
        cvt->len_mult *= (int) SDL_ceil(mult);
icculus@10756
   817
        cvt->len_ratio *= mult;
icculus@10756
   818
    } else {
icculus@10756
   819
        cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
icculus@10756
   820
    }
icculus@10756
   821
icculus@11508
   822
    /* !!! FIXME: remove this if we can get the resampler to work in-place again. */
icculus@11508
   823
    /* the buffer is big enough to hold the destination now, but
icculus@11508
   824
       we need it large enough to hold a separate scratch buffer. */
icculus@11508
   825
    cvt->len_mult *= 2;
icculus@11508
   826
icculus@10756
   827
    return 1;               /* added a converter. */
slouken@2716
   828
}
icculus@1982
   829
icculus@11097
   830
static SDL_bool
icculus@11097
   831
SDL_SupportedAudioFormat(const SDL_AudioFormat fmt)
icculus@11097
   832
{
icculus@11097
   833
    switch (fmt) {
icculus@11097
   834
        case AUDIO_U8:
icculus@11097
   835
        case AUDIO_S8:
icculus@11097
   836
        case AUDIO_U16LSB:
icculus@11097
   837
        case AUDIO_S16LSB:
icculus@11097
   838
        case AUDIO_U16MSB:
icculus@11097
   839
        case AUDIO_S16MSB:
icculus@11097
   840
        case AUDIO_S32LSB:
icculus@11097
   841
        case AUDIO_S32MSB:
icculus@11097
   842
        case AUDIO_F32LSB:
icculus@11097
   843
        case AUDIO_F32MSB:
icculus@11097
   844
            return SDL_TRUE;  /* supported. */
icculus@11097
   845
icculus@11097
   846
        default:
icculus@11097
   847
            break;
icculus@11097
   848
    }
icculus@11097
   849
icculus@11097
   850
    return SDL_FALSE;  /* unsupported. */
icculus@11097
   851
}
icculus@11097
   852
icculus@11097
   853
static SDL_bool
icculus@11097
   854
SDL_SupportedChannelCount(const int channels)
icculus@11097
   855
{
icculus@11097
   856
    switch (channels) {
icculus@11097
   857
        case 1:  /* mono */
icculus@11097
   858
        case 2:  /* stereo */
icculus@11097
   859
        case 4:  /* quad */
icculus@11097
   860
        case 6:  /* 5.1 */
icculus@11405
   861
        case 8:  /* 7.1 */
icculus@11405
   862
          return SDL_TRUE;  /* supported. */
icculus@11097
   863
icculus@11097
   864
        default:
icculus@11097
   865
            break;
icculus@11097
   866
    }
icculus@11097
   867
icculus@11097
   868
    return SDL_FALSE;  /* unsupported. */
icculus@11097
   869
}
icculus@11097
   870
icculus@1982
   871
icculus@1982
   872
/* Creates a set of audio filters to convert from one format to another.
icculus@11319
   873
   Returns 0 if no conversion is needed, 1 if the audio filter is set up,
icculus@11319
   874
   or -1 if an error like invalid parameter, unsupported format, etc. occurred.
slouken@0
   875
*/
slouken@1895
   876
slouken@1895
   877
int
slouken@1895
   878
SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
icculus@1982
   879
                  SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
icculus@1982
   880
                  SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
slouken@0
   881
{
aschiffler@6819
   882
    /* Sanity check target pointer */
aschiffler@6819
   883
    if (cvt == NULL) {
icculus@7037
   884
        return SDL_InvalidParamError("cvt");
aschiffler@6819
   885
    }
slouken@7191
   886
slouken@10767
   887
    /* Make sure we zero out the audio conversion before error checking */
slouken@10767
   888
    SDL_zerop(cvt);
slouken@10767
   889
icculus@11097
   890
    if (!SDL_SupportedAudioFormat(src_fmt)) {
icculus@7037
   891
        return SDL_SetError("Invalid source format");
icculus@11097
   892
    } else if (!SDL_SupportedAudioFormat(dst_fmt)) {
icculus@7037
   893
        return SDL_SetError("Invalid destination format");
icculus@11097
   894
    } else if (!SDL_SupportedChannelCount(src_channels)) {
icculus@11097
   895
        return SDL_SetError("Invalid source channels");
icculus@11097
   896
    } else if (!SDL_SupportedChannelCount(dst_channels)) {
icculus@11097
   897
        return SDL_SetError("Invalid destination channels");
slouken@12803
   898
    } else if (src_rate <= 0) {
slouken@12803
   899
        return SDL_SetError("Source rate is equal to or less than zero");
slouken@12803
   900
    } else if (dst_rate <= 0) {
slouken@12803
   901
        return SDL_SetError("Destination rate is equal to or less than zero");
slouken@12803
   902
    } else if (src_rate >= INT32_MAX / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) {
slouken@12803
   903
        return SDL_SetError("Source rate is too high");
slouken@12803
   904
    } else if (dst_rate >= INT32_MAX / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) {
slouken@12803
   905
        return SDL_SetError("Destination rate is too high");
icculus@1982
   906
    }
icculus@3021
   907
slouken@10579
   908
#if DEBUG_CONVERT
icculus@1982
   909
    printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
slouken@1985
   910
           src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
slouken@1985
   911
#endif
icculus@1982
   912
slouken@1895
   913
    /* Start off with no conversion necessary */
icculus@1982
   914
    cvt->src_format = src_fmt;
icculus@1982
   915
    cvt->dst_format = dst_fmt;
slouken@1895
   916
    cvt->needed = 0;
slouken@1895
   917
    cvt->filter_index = 0;
icculus@11508
   918
    SDL_zero(cvt->filters);
slouken@1895
   919
    cvt->len_mult = 1;
slouken@1895
   920
    cvt->len_ratio = 1.0;
icculus@3021
   921
    cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
slouken@1895
   922
slouken@11406
   923
    /* Make sure we've chosen audio conversion functions (MMX, scalar, etc.) */
slouken@11406
   924
    SDL_ChooseAudioConverters();
slouken@11406
   925
icculus@10575
   926
    /* Type conversion goes like this now:
icculus@10575
   927
        - byteswap to CPU native format first if necessary.
icculus@10575
   928
        - convert to native Float32 if necessary.
icculus@10575
   929
        - resample and change channel count if necessary.
icculus@10575
   930
        - convert back to native format.
icculus@10575
   931
        - byteswap back to foreign format if necessary.
icculus@10575
   932
icculus@10575
   933
       The expectation is we can process data faster in float32
icculus@10575
   934
       (possibly with SIMD), and making several passes over the same
icculus@10756
   935
       buffer is likely to be CPU cache-friendly, avoiding the
icculus@10575
   936
       biggest performance hit in modern times. Previously we had
icculus@10575
   937
       (script-generated) custom converters for every data type and
icculus@10575
   938
       it was a bloat on SDL compile times and final library size. */
icculus@10575
   939
slouken@10767
   940
    /* see if we can skip float conversion entirely. */
slouken@10767
   941
    if (src_rate == dst_rate && src_channels == dst_channels) {
slouken@10767
   942
        if (src_fmt == dst_fmt) {
slouken@10767
   943
            return 0;
slouken@10767
   944
        }
slouken@10767
   945
slouken@10767
   946
        /* just a byteswap needed? */
slouken@10767
   947
        if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) {
slouken@11096
   948
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) {
slouken@11096
   949
                return -1;
slouken@11096
   950
            }
slouken@10767
   951
            cvt->needed = 1;
slouken@10767
   952
            return 1;
slouken@10767
   953
        }
icculus@10575
   954
    }
icculus@10575
   955
icculus@1982
   956
    /* Convert data types, if necessary. Updates (cvt). */
slouken@10767
   957
    if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) {
slouken@1985
   958
        return -1;              /* shouldn't happen, but just in case... */
icculus@3021
   959
    }
slouken@0
   960
icculus@1982
   961
    /* Channel conversion */
icculus@11405
   962
    if (src_channels < dst_channels) {
icculus@11405
   963
        /* Upmixing */
icculus@11405
   964
        /* Mono -> Stereo [-> ...] */
slouken@1895
   965
        if ((src_channels == 1) && (dst_channels > 1)) {
slouken@11096
   966
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertMonoToStereo) < 0) {
slouken@11096
   967
                return -1;
slouken@11096
   968
            }
slouken@1895
   969
            cvt->len_mult *= 2;
slouken@1895
   970
            src_channels = 2;
slouken@1895
   971
            cvt->len_ratio *= 2;
slouken@1895
   972
        }
icculus@11405
   973
        /* [Mono ->] Stereo -> 5.1 [-> 7.1] */
icculus@11405
   974
        if ((src_channels == 2) && (dst_channels >= 6)) {
slouken@11096
   975
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertStereoTo51) < 0) {
slouken@11096
   976
                return -1;
slouken@11096
   977
            }
slouken@1895
   978
            src_channels = 6;
slouken@1895
   979
            cvt->len_mult *= 3;
slouken@1895
   980
            cvt->len_ratio *= 3;
slouken@1895
   981
        }
icculus@11405
   982
        /* Quad -> 5.1 [-> 7.1] */
icculus@11405
   983
        if ((src_channels == 4) && (dst_channels >= 6)) {
icculus@11405
   984
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertQuadTo51) < 0) {
icculus@11405
   985
                return -1;
icculus@11405
   986
            }
icculus@11405
   987
            src_channels = 6;
icculus@11405
   988
            cvt->len_mult = (cvt->len_mult * 3 + 1) / 2;
icculus@11405
   989
            cvt->len_ratio *= 1.5;
icculus@11405
   990
        }
icculus@11405
   991
        /* [[Mono ->] Stereo ->] 5.1 -> 7.1 */
icculus@11405
   992
        if ((src_channels == 6) && (dst_channels == 8)) {
icculus@11405
   993
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51To71) < 0) {
icculus@11405
   994
                return -1;
icculus@11405
   995
            }
icculus@11405
   996
            src_channels = 8;
icculus@11405
   997
            cvt->len_mult = (cvt->len_mult * 4 + 2) / 3;
icculus@11405
   998
            /* Should be numerically exact with every valid input to this
icculus@11405
   999
               function */
icculus@11405
  1000
            cvt->len_ratio = cvt->len_ratio * 4 / 3;
icculus@11405
  1001
        }
icculus@11405
  1002
        /* [Mono ->] Stereo -> Quad */
slouken@1895
  1003
        if ((src_channels == 2) && (dst_channels == 4)) {
slouken@11096
  1004
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertStereoToQuad) < 0) {
slouken@11096
  1005
                return -1;
slouken@11096
  1006
            }
slouken@1895
  1007
            src_channels = 4;
slouken@1895
  1008
            cvt->len_mult *= 2;
slouken@1895
  1009
            cvt->len_ratio *= 2;
slouken@1895
  1010
        }
icculus@11405
  1011
    } else if (src_channels > dst_channels) {
icculus@11405
  1012
        /* Downmixing */
icculus@11405
  1013
        /* 7.1 -> 5.1 [-> Stereo [-> Mono]] */
icculus@11405
  1014
        /* 7.1 -> 5.1 [-> Quad] */
icculus@11405
  1015
        if ((src_channels == 8) && (dst_channels <= 6)) {
icculus@11405
  1016
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert71To51) < 0) {
slouken@11096
  1017
                return -1;
slouken@11096
  1018
            }
icculus@11405
  1019
            src_channels = 6;
icculus@11405
  1020
            cvt->len_ratio *= 0.75;
slouken@1895
  1021
        }
icculus@11405
  1022
        /* [7.1 ->] 5.1 -> Stereo [-> Mono] */
slouken@1895
  1023
        if ((src_channels == 6) && (dst_channels <= 2)) {
slouken@11096
  1024
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51ToStereo) < 0) {
slouken@11096
  1025
                return -1;
slouken@11096
  1026
            }
slouken@1895
  1027
            src_channels = 2;
slouken@1895
  1028
            cvt->len_ratio /= 3;
slouken@1895
  1029
        }
icculus@11405
  1030
        /* 5.1 -> Quad */
slouken@1895
  1031
        if ((src_channels == 6) && (dst_channels == 4)) {
slouken@11096
  1032
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51ToQuad) < 0) {
slouken@11096
  1033
                return -1;
slouken@11096
  1034
            }
slouken@1895
  1035
            src_channels = 4;
icculus@11405
  1036
            cvt->len_ratio = cvt->len_ratio * 2 / 3;
icculus@11405
  1037
        }
icculus@11405
  1038
        /* Quad -> Stereo [-> Mono] */
icculus@11405
  1039
        if ((src_channels == 4) && (dst_channels <= 2)) {
icculus@11405
  1040
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertQuadToStereo) < 0) {
icculus@11405
  1041
                return -1;
icculus@11405
  1042
            }
icculus@11405
  1043
            src_channels = 2;
slouken@1895
  1044
            cvt->len_ratio /= 2;
slouken@1895
  1045
        }
icculus@11405
  1046
        /* [... ->] Stereo -> Mono */
icculus@11405
  1047
        if ((src_channels == 2) && (dst_channels == 1)) {
icculus@10832
  1048
            SDL_AudioFilter filter = NULL;
icculus@10832
  1049
icculus@10832
  1050
            #if HAVE_SSE3_INTRINSICS
icculus@10832
  1051
            if (SDL_HasSSE3()) {
icculus@10832
  1052
                filter = SDL_ConvertStereoToMono_SSE3;
icculus@10832
  1053
            }
icculus@10832
  1054
            #endif
icculus@10832
  1055
icculus@10832
  1056
            if (!filter) {
icculus@10832
  1057
                filter = SDL_ConvertStereoToMono;
icculus@10832
  1058
            }
icculus@10832
  1059
slouken@11096
  1060
            if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
slouken@11096
  1061
                return -1;
slouken@11096
  1062
            }
icculus@10832
  1063
icculus@11405
  1064
            src_channels = 1;
slouken@1895
  1065
            cvt->len_ratio /= 2;
slouken@1895
  1066
        }
slouken@1895
  1067
    }
slouken@0
  1068
icculus@11405
  1069
    if (src_channels != dst_channels) {
icculus@11405
  1070
        /* All combinations of supported channel counts should have been
icculus@11405
  1071
           handled by now, but let's be defensive */
icculus@11405
  1072
      return SDL_SetError("Invalid channel combination");
icculus@11405
  1073
    }
icculus@11405
  1074
    
icculus@3021
  1075
    /* Do rate conversion, if necessary. Updates (cvt). */
slouken@10767
  1076
    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
icculus@3021
  1077
        return -1;              /* shouldn't happen, but just in case... */
slouken@2716
  1078
    }
slouken@2716
  1079
icculus@10756
  1080
    /* Move to final data type. */
slouken@10767
  1081
    if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) {
icculus@10575
  1082
        return -1;              /* shouldn't happen, but just in case... */
slouken@1895
  1083
    }
icculus@10575
  1084
icculus@10575
  1085
    cvt->needed = (cvt->filter_index != 0);
slouken@1895
  1086
    return (cvt->needed);
slouken@1895
  1087
}
slouken@0
  1088
icculus@10842
  1089
typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const void *inbuf, const int inbuflen, void *outbuf, const int outbuflen);
slouken@10773
  1090
typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream);
slouken@10773
  1091
typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream);
icculus@10757
  1092
slouken@11631
  1093
struct _SDL_AudioStream
icculus@10757
  1094
{
icculus@10757
  1095
    SDL_AudioCVT cvt_before_resampling;
icculus@10757
  1096
    SDL_AudioCVT cvt_after_resampling;
icculus@10757
  1097
    SDL_DataQueue *queue;
icculus@11583
  1098
    SDL_bool first_run;
slouken@11632
  1099
    Uint8 *staging_buffer;
slouken@11632
  1100
    int staging_buffer_size;
slouken@11632
  1101
    int staging_buffer_filled;
icculus@10844
  1102
    Uint8 *work_buffer_base;  /* maybe unaligned pointer from SDL_realloc(). */
icculus@10757
  1103
    int work_buffer_len;
icculus@10757
  1104
    int src_sample_frame_size;
icculus@10757
  1105
    SDL_AudioFormat src_format;
icculus@10757
  1106
    Uint8 src_channels;
icculus@10757
  1107
    int src_rate;
icculus@10757
  1108
    int dst_sample_frame_size;
icculus@10757
  1109
    SDL_AudioFormat dst_format;
icculus@10757
  1110
    Uint8 dst_channels;
icculus@10757
  1111
    int dst_rate;
icculus@10757
  1112
    double rate_incr;
icculus@10757
  1113
    Uint8 pre_resample_channels;
slouken@10773
  1114
    int packetlen;
icculus@11583
  1115
    int resampler_padding_samples;
icculus@11583
  1116
    float *resampler_padding;
slouken@10773
  1117
    void *resampler_state;
slouken@10773
  1118
    SDL_ResampleAudioStreamFunc resampler_func;
slouken@10773
  1119
    SDL_ResetAudioStreamResamplerFunc reset_resampler_func;
slouken@10773
  1120
    SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func;
slouken@10773
  1121
};
slouken@10773
  1122
icculus@10851
  1123
static Uint8 *
icculus@10851
  1124
EnsureStreamBufferSize(SDL_AudioStream *stream, const int newlen)
icculus@10851
  1125
{
icculus@10851
  1126
    Uint8 *ptr;
icculus@10851
  1127
    size_t offset;
icculus@10851
  1128
icculus@10851
  1129
    if (stream->work_buffer_len >= newlen) {
icculus@10851
  1130
        ptr = stream->work_buffer_base;
icculus@10851
  1131
    } else {
icculus@10851
  1132
        ptr = (Uint8 *) SDL_realloc(stream->work_buffer_base, newlen + 32);
icculus@10851
  1133
        if (!ptr) {
icculus@10851
  1134
            SDL_OutOfMemory();
icculus@10851
  1135
            return NULL;
icculus@10851
  1136
        }
icculus@10851
  1137
        /* Make sure we're aligned to 16 bytes for SIMD code. */
icculus@10851
  1138
        stream->work_buffer_base = ptr;
icculus@10851
  1139
        stream->work_buffer_len = newlen;
icculus@10851
  1140
    }
icculus@10851
  1141
icculus@10851
  1142
    offset = ((size_t) ptr) & 15;
icculus@10851
  1143
    return offset ? ptr + (16 - offset) : ptr;
icculus@10851
  1144
}
icculus@10851
  1145
slouken@10777
  1146
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
  1147
static int
icculus@10842
  1148
SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen)
slouken@10773
  1149
{
icculus@10842
  1150
    const float *inbuf = (const float *) _inbuf;
icculus@10842
  1151
    float *outbuf = (float *) _outbuf;
icculus@10799
  1152
    const int framelen = sizeof(float) * stream->pre_resample_channels;
icculus@10790
  1153
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
  1154
    SRC_DATA data;
slouken@10773
  1155
    int result;
slouken@10773
  1156
icculus@11583
  1157
    SDL_assert(inbuf != ((const float *) outbuf));  /* SDL_AudioStreamPut() shouldn't allow in-place resamples. */
icculus@10851
  1158
slouken@10777
  1159
    data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
icculus@10799
  1160
    data.input_frames = inbuflen / framelen;
slouken@10773
  1161
    data.input_frames_used = 0;
slouken@10773
  1162
slouken@10773
  1163
    data.data_out = outbuf;
icculus@10799
  1164
    data.output_frames = outbuflen / framelen;
slouken@10773
  1165
slouken@10773
  1166
    data.end_of_input = 0;
slouken@10773
  1167
    data.src_ratio = stream->rate_incr;
slouken@10773
  1168
icculus@10790
  1169
    result = SRC_src_process(state, &data);
slouken@10773
  1170
    if (result != 0) {
icculus@10790
  1171
        SDL_SetError("src_process() failed: %s", SRC_src_strerror(result));
slouken@10773
  1172
        return 0;
slouken@10773
  1173
    }
slouken@10773
  1174
slouken@10773
  1175
    /* If this fails, we need to store them off somewhere */
slouken@10773
  1176
    SDL_assert(data.input_frames_used == data.input_frames);
slouken@10773
  1177
slouken@10773
  1178
    return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels);
slouken@10773
  1179
}
slouken@10773
  1180
slouken@10773
  1181
static void
slouken@10773
  1182
SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
  1183
{
icculus@10790
  1184
    SRC_src_reset((SRC_STATE *)stream->resampler_state);
slouken@10773
  1185
}
slouken@10773
  1186
slouken@10773
  1187
static void
slouken@10773
  1188
SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
  1189
{
icculus@10790
  1190
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
  1191
    if (state) {
icculus@10790
  1192
        SRC_src_delete(state);
slouken@10773
  1193
    }
slouken@10773
  1194
slouken@10773
  1195
    stream->resampler_state = NULL;
slouken@10773
  1196
    stream->resampler_func = NULL;
slouken@10773
  1197
    stream->reset_resampler_func = NULL;
slouken@10773
  1198
    stream->cleanup_resampler_func = NULL;
slouken@10773
  1199
}
slouken@10773
  1200
slouken@10773
  1201
static SDL_bool
slouken@10773
  1202
SetupLibSampleRateResampling(SDL_AudioStream *stream)
slouken@10773
  1203
{
icculus@10790
  1204
    int result = 0;
icculus@10790
  1205
    SRC_STATE *state = NULL;
slouken@10773
  1206
icculus@10790
  1207
    if (SRC_available) {
icculus@10849
  1208
        state = SRC_src_new(SRC_converter, stream->pre_resample_channels, &result);
icculus@10790
  1209
        if (!state) {
icculus@10790
  1210
            SDL_SetError("src_new() failed: %s", SRC_src_strerror(result));
icculus@10790
  1211
        }
slouken@10773
  1212
    }
slouken@10773
  1213
icculus@10790
  1214
    if (!state) {
icculus@10790
  1215
        SDL_CleanupAudioStreamResampler_SRC(stream);
slouken@10773
  1216
        return SDL_FALSE;
slouken@10773
  1217
    }
slouken@10773
  1218
slouken@10773
  1219
    stream->resampler_state = state;
slouken@10773
  1220
    stream->resampler_func = SDL_ResampleAudioStream_SRC;
slouken@10773
  1221
    stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC;
slouken@10773
  1222
    stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC;
slouken@10773
  1223
slouken@10773
  1224
    return SDL_TRUE;
slouken@10773
  1225
}
icculus@10790
  1226
#endif /* HAVE_LIBSAMPLERATE_H */
slouken@10773
  1227
slouken@10773
  1228
slouken@10773
  1229
static int
icculus@10842
  1230
SDL_ResampleAudioStream(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen)
slouken@10773
  1231
{
icculus@11583
  1232
    const Uint8 *inbufend = ((const Uint8 *) _inbuf) + inbuflen;
icculus@10842
  1233
    const float *inbuf = (const float *) _inbuf;
icculus@10842
  1234
    float *outbuf = (float *) _outbuf;
icculus@11517
  1235
    const int chans = (int) stream->pre_resample_channels;
icculus@11517
  1236
    const int inrate = stream->src_rate;
icculus@11517
  1237
    const int outrate = stream->dst_rate;
icculus@11583
  1238
    const int paddingsamples = stream->resampler_padding_samples;
icculus@11517
  1239
    const int paddingbytes = paddingsamples * sizeof (float);
icculus@11517
  1240
    float *lpadding = (float *) stream->resampler_state;
icculus@11583
  1241
    const float *rpadding = (const float *) inbufend; /* we set this up so there are valid padding samples at the end of the input buffer. */
icculus@11591
  1242
    const int cpy = SDL_min(inbuflen, paddingbytes);
icculus@11517
  1243
    int retval;
slouken@10773
  1244
icculus@11583
  1245
    SDL_assert(inbuf != ((const float *) outbuf));  /* SDL_AudioStreamPut() shouldn't allow in-place resamples. */
slouken@11519
  1246
icculus@11517
  1247
    retval = SDL_ResampleAudio(chans, inrate, outrate, lpadding, rpadding, inbuf, inbuflen, outbuf, outbuflen);
slouken@10773
  1248
icculus@11517
  1249
    /* update our left padding with end of current input, for next run. */
icculus@11591
  1250
    SDL_memcpy((lpadding + paddingsamples) - (cpy / sizeof (float)), inbufend - cpy, cpy);
icculus@11517
  1251
    return retval;
slouken@10773
  1252
}
slouken@10773
  1253
slouken@10773
  1254
static void
slouken@10773
  1255
SDL_ResetAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
  1256
{
icculus@11583
  1257
    /* set all the padding to silence. */
icculus@11583
  1258
    const int len = stream->resampler_padding_samples;
icculus@11517
  1259
    SDL_memset(stream->resampler_state, '\0', len * sizeof (float));
slouken@10773
  1260
}
slouken@10773
  1261
slouken@10773
  1262
static void
slouken@10773
  1263
SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
  1264
{
slouken@10773
  1265
    SDL_free(stream->resampler_state);
slouken@10773
  1266
}
icculus@10757
  1267
icculus@10789
  1268
SDL_AudioStream *
icculus@10789
  1269
SDL_NewAudioStream(const SDL_AudioFormat src_format,
icculus@10789
  1270
                   const Uint8 src_channels,
icculus@10789
  1271
                   const int src_rate,
icculus@10789
  1272
                   const SDL_AudioFormat dst_format,
icculus@10789
  1273
                   const Uint8 dst_channels,
icculus@10789
  1274
                   const int dst_rate)
icculus@10757
  1275
{
icculus@10757
  1276
    const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
icculus@10757
  1277
    Uint8 pre_resample_channels;
icculus@10757
  1278
    SDL_AudioStream *retval;
icculus@10757
  1279
icculus@10757
  1280
    retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
icculus@10757
  1281
    if (!retval) {
icculus@10757
  1282
        return NULL;
icculus@10757
  1283
    }
icculus@10757
  1284
icculus@10757
  1285
    /* If increasing channels, do it after resampling, since we'd just
icculus@10757
  1286
       do more work to resample duplicate channels. If we're decreasing, do
icculus@10757
  1287
       it first so we resample the interpolated data instead of interpolating
icculus@10757
  1288
       the resampled data (!!! FIXME: decide if that works in practice, though!). */
icculus@10757
  1289
    pre_resample_channels = SDL_min(src_channels, dst_channels);
icculus@10757
  1290
icculus@11583
  1291
    retval->first_run = SDL_TRUE;
icculus@10883
  1292
    retval->src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels;
icculus@10757
  1293
    retval->src_format = src_format;
icculus@10757
  1294
    retval->src_channels = src_channels;
icculus@10757
  1295
    retval->src_rate = src_rate;
icculus@10883
  1296
    retval->dst_sample_frame_size = (SDL_AUDIO_BITSIZE(dst_format) / 8) * dst_channels;
icculus@10757
  1297
    retval->dst_format = dst_format;
icculus@10757
  1298
    retval->dst_channels = dst_channels;
icculus@10757
  1299
    retval->dst_rate = dst_rate;
icculus@10757
  1300
    retval->pre_resample_channels = pre_resample_channels;
icculus@10757
  1301
    retval->packetlen = packetlen;
icculus@10757
  1302
    retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
icculus@11583
  1303
    retval->resampler_padding_samples = ResamplerPadding(retval->src_rate, retval->dst_rate) * pre_resample_channels;
icculus@12036
  1304
    retval->resampler_padding = (float *) SDL_calloc(retval->resampler_padding_samples ? retval->resampler_padding_samples : 1, sizeof (float));
icculus@11583
  1305
icculus@11583
  1306
    if (retval->resampler_padding == NULL) {
icculus@11583
  1307
        SDL_FreeAudioStream(retval);
icculus@11583
  1308
        SDL_OutOfMemory();
icculus@11583
  1309
        return NULL;
icculus@11583
  1310
    }
icculus@10757
  1311
slouken@11632
  1312
    retval->staging_buffer_size = ((retval->resampler_padding_samples / retval->pre_resample_channels) * retval->src_sample_frame_size);
slouken@11632
  1313
    if (retval->staging_buffer_size > 0) {
slouken@11632
  1314
        retval->staging_buffer = (Uint8 *) SDL_malloc(retval->staging_buffer_size);
icculus@11634
  1315
        if (retval->staging_buffer == NULL) {
slouken@11632
  1316
            SDL_FreeAudioStream(retval);
slouken@11632
  1317
            SDL_OutOfMemory();
slouken@11632
  1318
            return NULL;
slouken@11632
  1319
        }
slouken@11632
  1320
    }
slouken@11632
  1321
slouken@11632
  1322
    /* Not resampling? It's an easy conversion (and maybe not even that!) */
icculus@10757
  1323
    if (src_rate == dst_rate) {
icculus@10757
  1324
        retval->cvt_before_resampling.needed = SDL_FALSE;
slouken@10773
  1325
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
  1326
            SDL_FreeAudioStream(retval);
icculus@10757
  1327
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1328
        }
icculus@10757
  1329
    } else {
icculus@10757
  1330
        /* Don't resample at first. Just get us to Float32 format. */
icculus@10757
  1331
        /* !!! FIXME: convert to int32 on devices without hardware float. */
slouken@10773
  1332
        if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) {
slouken@10773
  1333
            SDL_FreeAudioStream(retval);
icculus@10757
  1334
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1335
        }
icculus@10757
  1336
slouken@10777
  1337
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
  1338
        SetupLibSampleRateResampling(retval);
slouken@10773
  1339
#endif
slouken@10773
  1340
slouken@10773
  1341
        if (!retval->resampler_func) {
icculus@11583
  1342
            retval->resampler_state = SDL_calloc(retval->resampler_padding_samples, sizeof (float));
slouken@10773
  1343
            if (!retval->resampler_state) {
slouken@10773
  1344
                SDL_FreeAudioStream(retval);
slouken@10773
  1345
                SDL_OutOfMemory();
slouken@10773
  1346
                return NULL;
slouken@10773
  1347
            }
icculus@11508
  1348
icculus@11508
  1349
            if (SDL_PrepareResampleFilter() < 0) {
icculus@11508
  1350
                SDL_free(retval->resampler_state);
icculus@11508
  1351
                retval->resampler_state = NULL;
icculus@11508
  1352
                SDL_FreeAudioStream(retval);
icculus@11508
  1353
                return NULL;
icculus@11508
  1354
            }
icculus@11508
  1355
slouken@10773
  1356
            retval->resampler_func = SDL_ResampleAudioStream;
slouken@10773
  1357
            retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
slouken@10773
  1358
            retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
slouken@10773
  1359
        }
slouken@10773
  1360
icculus@10757
  1361
        /* Convert us to the final format after resampling. */
slouken@10773
  1362
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
  1363
            SDL_FreeAudioStream(retval);
icculus@10757
  1364
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1365
        }
icculus@10757
  1366
    }
icculus@10757
  1367
icculus@10757
  1368
    retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
icculus@10757
  1369
    if (!retval->queue) {
slouken@10773
  1370
        SDL_FreeAudioStream(retval);
icculus@10757
  1371
        return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
icculus@10757
  1372
    }
icculus@10757
  1373
icculus@10757
  1374
    return retval;
icculus@10757
  1375
}
icculus@10757
  1376
slouken@11632
  1377
static int
icculus@11636
  1378
SDL_AudioStreamPutInternal(SDL_AudioStream *stream, const void *buf, int len, int *maxputbytes)
icculus@10757
  1379
{
slouken@11631
  1380
    int buflen = len;
icculus@11583
  1381
    int workbuflen;
icculus@11583
  1382
    Uint8 *workbuf;
icculus@11583
  1383
    Uint8 *resamplebuf = NULL;
icculus@11583
  1384
    int resamplebuflen = 0;
icculus@11590
  1385
    int neededpaddingbytes;
icculus@11583
  1386
    int paddingbytes;
icculus@10757
  1387
icculus@10844
  1388
    /* !!! FIXME: several converters can take advantage of SIMD, but only
icculus@10844
  1389
       !!! FIXME:  if the data is aligned to 16 bytes. EnsureStreamBufferSize()
icculus@10844
  1390
       !!! FIXME:  guarantees the buffer will align, but the
icculus@10844
  1391
       !!! FIXME:  converters will iterate over the data backwards if
icculus@10844
  1392
       !!! FIXME:  the output grows, and this means we won't align if buflen
icculus@10844
  1393
       !!! FIXME:  isn't a multiple of 16. In these cases, we should chop off
icculus@10844
  1394
       !!! FIXME:  a few samples at the end and convert them separately. */
icculus@10844
  1395
icculus@11583
  1396
    /* no padding prepended on first run. */
icculus@11590
  1397
    neededpaddingbytes = stream->resampler_padding_samples * sizeof (float);
icculus@11583
  1398
    paddingbytes = stream->first_run ? 0 : neededpaddingbytes;
icculus@11583
  1399
    stream->first_run = SDL_FALSE;
icculus@11583
  1400
icculus@11583
  1401
    /* Make sure the work buffer can hold all the data we need at once... */
icculus@11583
  1402
    workbuflen = buflen;
icculus@11583
  1403
    if (stream->cvt_before_resampling.needed) {
icculus@11583
  1404
        workbuflen *= stream->cvt_before_resampling.len_mult;
icculus@11583
  1405
    }
icculus@11583
  1406
icculus@11583
  1407
    if (stream->dst_rate != stream->src_rate) {
icculus@11583
  1408
        /* resamples can't happen in place, so make space for second buf. */
icculus@11583
  1409
        const int framesize = stream->pre_resample_channels * sizeof (float);
icculus@11583
  1410
        const int frames = workbuflen / framesize;
icculus@11583
  1411
        resamplebuflen = ((int) SDL_ceil(frames * stream->rate_incr)) * framesize;
icculus@11583
  1412
        #if DEBUG_AUDIOSTREAM
icculus@11583
  1413
        printf("AUDIOSTREAM: will resample %d bytes to %d (ratio=%.6f)\n", workbuflen, resamplebuflen, stream->rate_incr);
icculus@11583
  1414
        #endif
icculus@11583
  1415
        workbuflen += resamplebuflen;
icculus@11583
  1416
    }
icculus@11583
  1417
icculus@11583
  1418
    if (stream->cvt_after_resampling.needed) {
icculus@11583
  1419
        /* !!! FIXME: buffer might be big enough already? */
icculus@11583
  1420
        workbuflen *= stream->cvt_after_resampling.len_mult;
icculus@11583
  1421
    }
icculus@11583
  1422
icculus@11583
  1423
    workbuflen += neededpaddingbytes;
icculus@11583
  1424
icculus@11583
  1425
    #if DEBUG_AUDIOSTREAM
icculus@11583
  1426
    printf("AUDIOSTREAM: Putting %d bytes of preconverted audio, need %d byte work buffer\n", buflen, workbuflen);
icculus@11583
  1427
    #endif
icculus@11583
  1428
icculus@11583
  1429
    workbuf = EnsureStreamBufferSize(stream, workbuflen);
icculus@11583
  1430
    if (!workbuf) {
icculus@11583
  1431
        return -1;  /* probably out of memory. */
icculus@11583
  1432
    }
icculus@11583
  1433
icculus@11583
  1434
    resamplebuf = workbuf;  /* default if not resampling. */
icculus@11583
  1435
icculus@11583
  1436
    SDL_memcpy(workbuf + paddingbytes, buf, buflen);
icculus@11583
  1437
icculus@10757
  1438
    if (stream->cvt_before_resampling.needed) {
icculus@11583
  1439
        stream->cvt_before_resampling.buf = workbuf + paddingbytes;
icculus@10757
  1440
        stream->cvt_before_resampling.len = buflen;
icculus@10757
  1441
        if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
icculus@10757
  1442
            return -1;   /* uhoh! */
icculus@10757
  1443
        }
icculus@10757
  1444
        buflen = stream->cvt_before_resampling.len_cvt;
icculus@11583
  1445
icculus@11583
  1446
        #if DEBUG_AUDIOSTREAM
icculus@11583
  1447
        printf("AUDIOSTREAM: After initial conversion we have %d bytes\n", buflen);
icculus@11583
  1448
        #endif
icculus@10757
  1449
    }
icculus@10757
  1450
icculus@10757
  1451
    if (stream->dst_rate != stream->src_rate) {
icculus@11583
  1452
        /* save off some samples at the end; they are used for padding now so
icculus@11583
  1453
           the resampler is coherent and then used at the start of the next
icculus@11583
  1454
           put operation. Prepend last put operation's padding, too. */
icculus@11583
  1455
icculus@11583
  1456
        /* prepend prior put's padding. :P */
icculus@11583
  1457
        if (paddingbytes) {
icculus@11583
  1458
            SDL_memcpy(workbuf, stream->resampler_padding, paddingbytes);
icculus@11583
  1459
            buflen += paddingbytes;
icculus@10757
  1460
        }
icculus@11583
  1461
icculus@11583
  1462
        /* save off the data at the end for the next run. */
icculus@11583
  1463
        SDL_memcpy(stream->resampler_padding, workbuf + (buflen - neededpaddingbytes), neededpaddingbytes);
icculus@11583
  1464
icculus@11583
  1465
        resamplebuf = workbuf + buflen;  /* skip to second piece of workbuf. */
icculus@11591
  1466
        SDL_assert(buflen >= neededpaddingbytes);
icculus@11591
  1467
        if (buflen > neededpaddingbytes) {
icculus@11591
  1468
            buflen = stream->resampler_func(stream, workbuf, buflen - neededpaddingbytes, resamplebuf, resamplebuflen);
icculus@11591
  1469
        } else {
icculus@11591
  1470
            buflen = 0;
icculus@11591
  1471
        }
icculus@11583
  1472
icculus@11583
  1473
        #if DEBUG_AUDIOSTREAM
icculus@11583
  1474
        printf("AUDIOSTREAM: After resampling we have %d bytes\n", buflen);
icculus@11583
  1475
        #endif
icculus@10757
  1476
    }
icculus@10757
  1477
icculus@11591
  1478
    if (stream->cvt_after_resampling.needed && (buflen > 0)) {
icculus@11583
  1479
        stream->cvt_after_resampling.buf = resamplebuf;
icculus@10757
  1480
        stream->cvt_after_resampling.len = buflen;
icculus@10757
  1481
        if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
icculus@10757
  1482
            return -1;   /* uhoh! */
icculus@10757
  1483
        }
icculus@10757
  1484
        buflen = stream->cvt_after_resampling.len_cvt;
icculus@11583
  1485
icculus@11583
  1486
        #if DEBUG_AUDIOSTREAM
icculus@11583
  1487
        printf("AUDIOSTREAM: After final conversion we have %d bytes\n", buflen);
icculus@11583
  1488
        #endif
icculus@10757
  1489
    }
icculus@10757
  1490
icculus@11583
  1491
    #if DEBUG_AUDIOSTREAM
icculus@11583
  1492
    printf("AUDIOSTREAM: Final output is %d bytes\n", buflen);
icculus@11583
  1493
    #endif
icculus@11583
  1494
icculus@11636
  1495
    if (maxputbytes) {
icculus@11636
  1496
        const int maxbytes = *maxputbytes;
icculus@11636
  1497
        if (buflen > maxbytes)
icculus@11636
  1498
            buflen = maxbytes;
icculus@11636
  1499
        *maxputbytes -= buflen;
icculus@11636
  1500
    }
icculus@11636
  1501
icculus@11583
  1502
    /* resamplebuf holds the final output, even if we didn't resample. */
icculus@11591
  1503
    return buflen ? SDL_WriteToDataQueue(stream->queue, resamplebuf, buflen) : 0;
icculus@10757
  1504
}
icculus@10757
  1505
slouken@11632
  1506
int
slouken@11632
  1507
SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, int len)
slouken@11632
  1508
{
slouken@11632
  1509
    /* !!! FIXME: several converters can take advantage of SIMD, but only
slouken@11632
  1510
       !!! FIXME:  if the data is aligned to 16 bytes. EnsureStreamBufferSize()
slouken@11632
  1511
       !!! FIXME:  guarantees the buffer will align, but the
slouken@11632
  1512
       !!! FIXME:  converters will iterate over the data backwards if
slouken@11632
  1513
       !!! FIXME:  the output grows, and this means we won't align if buflen
slouken@11632
  1514
       !!! FIXME:  isn't a multiple of 16. In these cases, we should chop off
slouken@11632
  1515
       !!! FIXME:  a few samples at the end and convert them separately. */
slouken@11632
  1516
slouken@11632
  1517
    #if DEBUG_AUDIOSTREAM
slouken@11632
  1518
    printf("AUDIOSTREAM: wants to put %d preconverted bytes\n", buflen);
slouken@11632
  1519
    #endif
slouken@11632
  1520
slouken@11632
  1521
    if (!stream) {
slouken@11632
  1522
        return SDL_InvalidParamError("stream");
slouken@11632
  1523
    } else if (!buf) {
slouken@11632
  1524
        return SDL_InvalidParamError("buf");
slouken@11632
  1525
    } else if (len == 0) {
slouken@11632
  1526
        return 0;  /* nothing to do. */
slouken@11632
  1527
    } else if ((len % stream->src_sample_frame_size) != 0) {
slouken@11632
  1528
        return SDL_SetError("Can't add partial sample frames");
slouken@11632
  1529
    }
slouken@11632
  1530
slouken@11632
  1531
    if (!stream->cvt_before_resampling.needed &&
slouken@11632
  1532
        (stream->dst_rate == stream->src_rate) &&
slouken@11632
  1533
        !stream->cvt_after_resampling.needed) {
slouken@11632
  1534
        #if DEBUG_AUDIOSTREAM
slouken@11632
  1535
        printf("AUDIOSTREAM: no conversion needed at all, queueing %d bytes.\n", len);
slouken@11632
  1536
        #endif
slouken@11632
  1537
        return SDL_WriteToDataQueue(stream->queue, buf, len);
slouken@11632
  1538
    }
slouken@11632
  1539
slouken@11632
  1540
    while (len > 0) {
slouken@11632
  1541
        int amount;
slouken@11632
  1542
slouken@11632
  1543
        /* If we don't have a staging buffer or we're given enough data that
slouken@11632
  1544
           we don't need to store it for later, skip the staging process.
slouken@11632
  1545
         */
slouken@11632
  1546
        if (!stream->staging_buffer_filled && len >= stream->staging_buffer_size) {
icculus@11636
  1547
            return SDL_AudioStreamPutInternal(stream, buf, len, NULL);
slouken@11632
  1548
        }
slouken@11632
  1549
slouken@11632
  1550
        /* If there's not enough data to fill the staging buffer, just save it */
slouken@11632
  1551
        if ((stream->staging_buffer_filled + len) < stream->staging_buffer_size) {
slouken@11632
  1552
            SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, len);
slouken@11632
  1553
            stream->staging_buffer_filled += len;
slouken@11632
  1554
            return 0;
slouken@11632
  1555
        }
slouken@11632
  1556
 
slouken@11632
  1557
        /* Fill the staging buffer, process it, and continue */
slouken@11632
  1558
        amount = (stream->staging_buffer_size - stream->staging_buffer_filled);
slouken@11632
  1559
        SDL_assert(amount > 0);
slouken@11632
  1560
        SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, amount);
slouken@11632
  1561
        stream->staging_buffer_filled = 0;
icculus@11636
  1562
        if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, NULL) < 0) {
slouken@11632
  1563
            return -1;
slouken@11632
  1564
        }
slouken@11632
  1565
        buf = (void *)((Uint8 *)buf + amount);
slouken@11632
  1566
        len -= amount;
slouken@11632
  1567
    }
slouken@11632
  1568
    return 0;
slouken@11632
  1569
}
slouken@11632
  1570
icculus@11636
  1571
int SDL_AudioStreamFlush(SDL_AudioStream *stream)
icculus@11636
  1572
{
icculus@11636
  1573
    if (!stream) {
icculus@11636
  1574
        return SDL_InvalidParamError("stream");
icculus@11636
  1575
    }
icculus@11636
  1576
icculus@11636
  1577
    #if DEBUG_AUDIOSTREAM
icculus@11636
  1578
    printf("AUDIOSTREAM: flushing! staging_buffer_filled=%d bytes\n", stream->staging_buffer_filled);
icculus@11636
  1579
    #endif
icculus@11636
  1580
icculus@11636
  1581
    /* shouldn't use a staging buffer if we're not resampling. */
icculus@11636
  1582
    SDL_assert((stream->dst_rate != stream->src_rate) || (stream->staging_buffer_filled == 0));
icculus@11636
  1583
icculus@11636
  1584
    if (stream->staging_buffer_filled > 0) {
icculus@11636
  1585
        /* push the staging buffer + silence. We need to flush out not just
icculus@11636
  1586
           the staging buffer, but the piece that the stream was saving off
icculus@11636
  1587
           for right-side resampler padding. */
icculus@11636
  1588
        const SDL_bool first_run = stream->first_run;
icculus@11636
  1589
        const int filled = stream->staging_buffer_filled;
icculus@11636
  1590
        int actual_input_frames = filled / stream->src_sample_frame_size;
icculus@11636
  1591
        if (!first_run)
icculus@11636
  1592
            actual_input_frames += stream->resampler_padding_samples / stream->pre_resample_channels;
icculus@11636
  1593
icculus@11636
  1594
        if (actual_input_frames > 0) {  /* don't bother if nothing to flush. */
icculus@11636
  1595
            /* This is how many bytes we're expecting without silence appended. */
icculus@11636
  1596
            int flush_remaining = ((int) SDL_ceil(actual_input_frames * stream->rate_incr)) * stream->dst_sample_frame_size;
icculus@11636
  1597
icculus@11636
  1598
            #if DEBUG_AUDIOSTREAM
icculus@11636
  1599
            printf("AUDIOSTREAM: flushing with padding to get max %d bytes!\n", flush_remaining);
icculus@11636
  1600
            #endif
icculus@11636
  1601
icculus@11636
  1602
            SDL_memset(stream->staging_buffer + filled, '\0', stream->staging_buffer_size - filled);
icculus@11636
  1603
            if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, &flush_remaining) < 0) {
icculus@11636
  1604
                return -1;
icculus@11636
  1605
            }
icculus@11636
  1606
icculus@11636
  1607
            /* we have flushed out (or initially filled) the pending right-side
icculus@11636
  1608
               resampler padding, but we need to push more silence to guarantee
icculus@11636
  1609
               the staging buffer is fully flushed out, too. */
icculus@11636
  1610
            SDL_memset(stream->staging_buffer, '\0', filled);
icculus@11636
  1611
            if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, &flush_remaining) < 0) {
icculus@11636
  1612
                return -1;
icculus@11636
  1613
            }
icculus@11636
  1614
        }
icculus@11636
  1615
    }
icculus@11636
  1616
icculus@11636
  1617
    stream->staging_buffer_filled = 0;
icculus@11636
  1618
    stream->first_run = SDL_TRUE;
icculus@11636
  1619
icculus@11636
  1620
    return 0;
icculus@11636
  1621
}
icculus@11636
  1622
icculus@10757
  1623
/* get converted/resampled data from the stream */
icculus@10757
  1624
int
slouken@11631
  1625
SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, int len)
icculus@10757
  1626
{
icculus@11583
  1627
    #if DEBUG_AUDIOSTREAM
slouken@11631
  1628
    printf("AUDIOSTREAM: want to get %d converted bytes\n", len);
icculus@11583
  1629
    #endif
icculus@11583
  1630
icculus@10757
  1631
    if (!stream) {
icculus@10757
  1632
        return SDL_InvalidParamError("stream");
icculus@10757
  1633
    } else if (!buf) {
icculus@10757
  1634
        return SDL_InvalidParamError("buf");
slouken@11631
  1635
    } else if (len <= 0) {
icculus@10757
  1636
        return 0;  /* nothing to do. */
icculus@10757
  1637
    } else if ((len % stream->dst_sample_frame_size) != 0) {
icculus@10757
  1638
        return SDL_SetError("Can't request partial sample frames");
icculus@10757
  1639
    }
icculus@10757
  1640
icculus@10764
  1641
    return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
icculus@10757
  1642
}
icculus@10757
  1643
icculus@10757
  1644
/* number of converted/resampled bytes available */
icculus@10757
  1645
int
icculus@10757
  1646
SDL_AudioStreamAvailable(SDL_AudioStream *stream)
icculus@10757
  1647
{
icculus@10757
  1648
    return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
icculus@10757
  1649
}
icculus@10757
  1650
slouken@11631
  1651
void
slouken@11631
  1652
SDL_AudioStreamClear(SDL_AudioStream *stream)
slouken@11631
  1653
{
slouken@11631
  1654
    if (!stream) {
slouken@11631
  1655
        SDL_InvalidParamError("stream");
slouken@11631
  1656
    } else {
slouken@11631
  1657
        SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
slouken@11631
  1658
        if (stream->reset_resampler_func) {
slouken@11631
  1659
            stream->reset_resampler_func(stream);
slouken@11631
  1660
        }
slouken@11631
  1661
        stream->first_run = SDL_TRUE;
icculus@11636
  1662
        stream->staging_buffer_filled = 0;
slouken@11631
  1663
    }
slouken@11631
  1664
}
slouken@11631
  1665
icculus@10757
  1666
/* dispose of a stream */
icculus@10757
  1667
void
icculus@10757
  1668
SDL_FreeAudioStream(SDL_AudioStream *stream)
icculus@10757
  1669
{
icculus@10757
  1670
    if (stream) {
slouken@10773
  1671
        if (stream->cleanup_resampler_func) {
slouken@10773
  1672
            stream->cleanup_resampler_func(stream);
slouken@10773
  1673
        }
icculus@10757
  1674
        SDL_FreeDataQueue(stream->queue);
slouken@11632
  1675
        SDL_free(stream->staging_buffer);
icculus@10844
  1676
        SDL_free(stream->work_buffer_base);
icculus@11583
  1677
        SDL_free(stream->resampler_padding);
icculus@10757
  1678
        SDL_free(stream);
icculus@10757
  1679
    }
icculus@10757
  1680
}
icculus@10757
  1681
icculus@10575
  1682
/* vi: set ts=4 sw=4 expandtab: */
slouken@2716
  1683