src/audio/SDL_audiocvt.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 20 Oct 2017 16:53:42 -0700
changeset 11642 e081e63addd3
parent 11641 e4ffc5a1b7ea
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Fixed typo converting 4 channel audio to 2 channel
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@0
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
icculus@8093
    21
#include "../SDL_internal.h"
slouken@2728
    22
slouken@0
    23
/* Functions for audio drivers to perform runtime conversion of audio format */
slouken@0
    24
slouken@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"
icculus@6281
    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);
slouken@0
    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@942
   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
    }
slouken@942
   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@942
   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@0
   330
}
slouken@0
   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@10575
   564
{
slouken@10579
   565
#if DEBUG_CONVERT
slouken@10579
   566
    printf("Converting byte order\n");
slouken@10579
   567
#endif
icculus@1982
   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)
icculus@10575
   663
{
icculus@10575
   664
    int retval = 0;  /* 0 == no conversion necessary. */
icculus@3021
   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
        }
slouken@2716
   678
icculus@10575
   679
        if (!filter) {
icculus@11319
   680
            return SDL_SetError("No conversion from float to destination format available");
icculus@10575
   681
        }
icculus@10575
   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;
icculus@3021
   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;
icculus@11517
   721
    const int paddingsamples = (ResamplerPadding(inrate, outrate) * chans);
slouken@11519
   722
    float *padding;
icculus@10756
   723
icculus@10799
   724
    SDL_assert(format == AUDIO_F32SYS);
icculus@10799
   725
icculus@11517
   726
    /* we keep no streaming state here, so pad with silence on both ends. */
icculus@11585
   727
    padding = (float *) SDL_calloc(paddingsamples, sizeof (float));
slouken@11519
   728
    if (!padding) {
slouken@11519
   729
        SDL_OutOfMemory();
slouken@11519
   730
        return;
slouken@11519
   731
    }
icculus@10799
   732
icculus@11517
   733
    cvt->len_cvt = SDL_ResampleAudio(chans, inrate, outrate, padding, padding, src, srclen, dst, dstlen);
icculus@11508
   734
icculus@11585
   735
    SDL_free(padding);
slouken@11519
   736
icculus@11586
   737
    SDL_memmove(cvt->buf, dst, cvt->len_cvt);  /* !!! FIXME: remove this if we can get the resampler to work in-place again. */
icculus@11508
   738
icculus@10799
   739
    if (cvt->filters[++cvt->filter_index]) {
icculus@10799
   740
        cvt->filters[cvt->filter_index](cvt, format);
icculus@10799
   741
    }
icculus@10799
   742
}
icculus@10799
   743
icculus@10799
   744
/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't
icculus@10799
   745
   !!! FIXME:  store channel info, so we have to have function entry
icculus@10799
   746
   !!! FIXME:  points for each supported channel count and multiple
icculus@10799
   747
   !!! FIXME:  vs arbitrary. When we rev the ABI, clean this up. */
icculus@10756
   748
#define RESAMPLER_FUNCS(chans) \
icculus@10756
   749
    static void SDLCALL \
icculus@10799
   750
    SDL_ResampleCVT_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
icculus@10799
   751
        SDL_ResampleCVT(cvt, chans, format); \
icculus@10756
   752
    }
icculus@10756
   753
RESAMPLER_FUNCS(1)
icculus@10756
   754
RESAMPLER_FUNCS(2)
icculus@10756
   755
RESAMPLER_FUNCS(4)
icculus@10756
   756
RESAMPLER_FUNCS(6)
icculus@10756
   757
RESAMPLER_FUNCS(8)
icculus@10756
   758
#undef RESAMPLER_FUNCS
icculus@10756
   759
icculus@10799
   760
static SDL_AudioFilter
icculus@10799
   761
ChooseCVTResampler(const int dst_channels)
icculus@3021
   762
{
icculus@10799
   763
    switch (dst_channels) {
icculus@10799
   764
        case 1: return SDL_ResampleCVT_c1;
icculus@10799
   765
        case 2: return SDL_ResampleCVT_c2;
icculus@10799
   766
        case 4: return SDL_ResampleCVT_c4;
icculus@10799
   767
        case 6: return SDL_ResampleCVT_c6;
icculus@10799
   768
        case 8: return SDL_ResampleCVT_c8;
icculus@10799
   769
        default: break;
icculus@3021
   770
    }
slouken@2716
   771
icculus@10799
   772
    return NULL;
icculus@10756
   773
}
icculus@10575
   774
icculus@3021
   775
static int
icculus@10756
   776
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
icculus@10756
   777
                          const int src_rate, const int dst_rate)
icculus@3021
   778
{
icculus@10756
   779
    SDL_AudioFilter filter;
icculus@3021
   780
icculus@10756
   781
    if (src_rate == dst_rate) {
icculus@10756
   782
        return 0;  /* no conversion necessary. */
slouken@2716
   783
    }
slouken@2716
   784
icculus@10799
   785
    filter = ChooseCVTResampler(dst_channels);
icculus@10756
   786
    if (filter == NULL) {
icculus@10756
   787
        return SDL_SetError("No conversion available for these rates");
icculus@10756
   788
    }
icculus@10756
   789
icculus@11508
   790
    if (SDL_PrepareResampleFilter() < 0) {
icculus@11508
   791
        return -1;
icculus@11508
   792
    }
icculus@11508
   793
icculus@10756
   794
    /* Update (cvt) with filter details... */
slouken@11096
   795
    if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
slouken@11096
   796
        return -1;
slouken@11096
   797
    }
icculus@11508
   798
icculus@11508
   799
    /* !!! 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
   800
       !!! 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
   801
       !!! FIXME in 2.1:   so we steal the ninth and tenth slot.  :( */
icculus@11508
   802
    if (cvt->filter_index >= (SDL_AUDIOCVT_MAX_FILTERS-2)) {
icculus@11508
   803
        return SDL_SetError("Too many filters needed for conversion, exceeded maximum of %d", SDL_AUDIOCVT_MAX_FILTERS-2);
icculus@11508
   804
    }
icculus@11508
   805
    cvt->filters[SDL_AUDIOCVT_MAX_FILTERS-1] = (SDL_AudioFilter) (size_t) src_rate;
icculus@11508
   806
    cvt->filters[SDL_AUDIOCVT_MAX_FILTERS] = (SDL_AudioFilter) (size_t) dst_rate;
icculus@11508
   807
icculus@10756
   808
    if (src_rate < dst_rate) {
icculus@10756
   809
        const double mult = ((double) dst_rate) / ((double) src_rate);
icculus@10756
   810
        cvt->len_mult *= (int) SDL_ceil(mult);
icculus@10756
   811
        cvt->len_ratio *= mult;
icculus@10756
   812
    } else {
icculus@10756
   813
        cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
icculus@10756
   814
    }
icculus@10756
   815
icculus@11508
   816
    /* !!! FIXME: remove this if we can get the resampler to work in-place again. */
icculus@11508
   817
    /* the buffer is big enough to hold the destination now, but
icculus@11508
   818
       we need it large enough to hold a separate scratch buffer. */
icculus@11508
   819
    cvt->len_mult *= 2;
icculus@11508
   820
icculus@10756
   821
    return 1;               /* added a converter. */
slouken@2716
   822
}
icculus@1982
   823
icculus@11097
   824
static SDL_bool
icculus@11097
   825
SDL_SupportedAudioFormat(const SDL_AudioFormat fmt)
icculus@11097
   826
{
icculus@11097
   827
    switch (fmt) {
icculus@11097
   828
        case AUDIO_U8:
icculus@11097
   829
        case AUDIO_S8:
icculus@11097
   830
        case AUDIO_U16LSB:
icculus@11097
   831
        case AUDIO_S16LSB:
icculus@11097
   832
        case AUDIO_U16MSB:
icculus@11097
   833
        case AUDIO_S16MSB:
icculus@11097
   834
        case AUDIO_S32LSB:
icculus@11097
   835
        case AUDIO_S32MSB:
icculus@11097
   836
        case AUDIO_F32LSB:
icculus@11097
   837
        case AUDIO_F32MSB:
icculus@11097
   838
            return SDL_TRUE;  /* supported. */
icculus@11097
   839
icculus@11097
   840
        default:
icculus@11097
   841
            break;
icculus@11097
   842
    }
icculus@11097
   843
icculus@11097
   844
    return SDL_FALSE;  /* unsupported. */
icculus@11097
   845
}
icculus@11097
   846
icculus@11097
   847
static SDL_bool
icculus@11097
   848
SDL_SupportedChannelCount(const int channels)
icculus@11097
   849
{
icculus@11097
   850
    switch (channels) {
icculus@11097
   851
        case 1:  /* mono */
icculus@11097
   852
        case 2:  /* stereo */
icculus@11097
   853
        case 4:  /* quad */
icculus@11097
   854
        case 6:  /* 5.1 */
icculus@11405
   855
        case 8:  /* 7.1 */
icculus@11405
   856
          return SDL_TRUE;  /* supported. */
icculus@11097
   857
icculus@11097
   858
        default:
icculus@11097
   859
            break;
icculus@11097
   860
    }
icculus@11097
   861
icculus@11097
   862
    return SDL_FALSE;  /* unsupported. */
icculus@11097
   863
}
icculus@11097
   864
icculus@1982
   865
icculus@1982
   866
/* Creates a set of audio filters to convert from one format to another.
icculus@11319
   867
   Returns 0 if no conversion is needed, 1 if the audio filter is set up,
icculus@11319
   868
   or -1 if an error like invalid parameter, unsupported format, etc. occurred.
slouken@0
   869
*/
slouken@1895
   870
slouken@1895
   871
int
slouken@1895
   872
SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
icculus@1982
   873
                  SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
icculus@1982
   874
                  SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
slouken@0
   875
{
aschiffler@6819
   876
    /* Sanity check target pointer */
aschiffler@6819
   877
    if (cvt == NULL) {
icculus@7037
   878
        return SDL_InvalidParamError("cvt");
aschiffler@6819
   879
    }
slouken@7191
   880
slouken@10767
   881
    /* Make sure we zero out the audio conversion before error checking */
slouken@10767
   882
    SDL_zerop(cvt);
slouken@10767
   883
icculus@11097
   884
    if (!SDL_SupportedAudioFormat(src_fmt)) {
icculus@7037
   885
        return SDL_SetError("Invalid source format");
icculus@11097
   886
    } else if (!SDL_SupportedAudioFormat(dst_fmt)) {
icculus@7037
   887
        return SDL_SetError("Invalid destination format");
icculus@11097
   888
    } else if (!SDL_SupportedChannelCount(src_channels)) {
icculus@11097
   889
        return SDL_SetError("Invalid source channels");
icculus@11097
   890
    } else if (!SDL_SupportedChannelCount(dst_channels)) {
icculus@11097
   891
        return SDL_SetError("Invalid destination channels");
icculus@11097
   892
    } else if (src_rate == 0) {
icculus@11097
   893
        return SDL_SetError("Source rate is zero");
icculus@11097
   894
    } else if (dst_rate == 0) {
icculus@11097
   895
        return SDL_SetError("Destination rate is zero");
icculus@1982
   896
    }
icculus@3021
   897
slouken@10579
   898
#if DEBUG_CONVERT
icculus@1982
   899
    printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
slouken@1985
   900
           src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
slouken@1985
   901
#endif
icculus@1982
   902
slouken@1895
   903
    /* Start off with no conversion necessary */
icculus@1982
   904
    cvt->src_format = src_fmt;
icculus@1982
   905
    cvt->dst_format = dst_fmt;
slouken@1895
   906
    cvt->needed = 0;
slouken@1895
   907
    cvt->filter_index = 0;
icculus@11508
   908
    SDL_zero(cvt->filters);
slouken@1895
   909
    cvt->len_mult = 1;
slouken@1895
   910
    cvt->len_ratio = 1.0;
icculus@3021
   911
    cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
slouken@0
   912
slouken@11406
   913
    /* Make sure we've chosen audio conversion functions (MMX, scalar, etc.) */
slouken@11406
   914
    SDL_ChooseAudioConverters();
slouken@11406
   915
icculus@10575
   916
    /* Type conversion goes like this now:
icculus@10575
   917
        - byteswap to CPU native format first if necessary.
icculus@10575
   918
        - convert to native Float32 if necessary.
icculus@10575
   919
        - resample and change channel count if necessary.
icculus@10575
   920
        - convert back to native format.
icculus@10575
   921
        - byteswap back to foreign format if necessary.
icculus@10575
   922
icculus@10575
   923
       The expectation is we can process data faster in float32
icculus@10575
   924
       (possibly with SIMD), and making several passes over the same
icculus@10756
   925
       buffer is likely to be CPU cache-friendly, avoiding the
icculus@10575
   926
       biggest performance hit in modern times. Previously we had
icculus@10575
   927
       (script-generated) custom converters for every data type and
icculus@10575
   928
       it was a bloat on SDL compile times and final library size. */
icculus@10575
   929
slouken@10767
   930
    /* see if we can skip float conversion entirely. */
slouken@10767
   931
    if (src_rate == dst_rate && src_channels == dst_channels) {
slouken@10767
   932
        if (src_fmt == dst_fmt) {
slouken@10767
   933
            return 0;
slouken@10767
   934
        }
slouken@10767
   935
slouken@10767
   936
        /* just a byteswap needed? */
slouken@10767
   937
        if ((src_fmt & ~SDL_AUDIO_MASK_ENDIAN) == (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN)) {
slouken@11096
   938
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) {
slouken@11096
   939
                return -1;
slouken@11096
   940
            }
slouken@10767
   941
            cvt->needed = 1;
slouken@10767
   942
            return 1;
slouken@10767
   943
        }
icculus@10575
   944
    }
icculus@10575
   945
icculus@1982
   946
    /* Convert data types, if necessary. Updates (cvt). */
slouken@10767
   947
    if (SDL_BuildAudioTypeCVTToFloat(cvt, src_fmt) < 0) {
slouken@1985
   948
        return -1;              /* shouldn't happen, but just in case... */
icculus@3021
   949
    }
slouken@0
   950
icculus@1982
   951
    /* Channel conversion */
icculus@11405
   952
    if (src_channels < dst_channels) {
icculus@11405
   953
        /* Upmixing */
icculus@11405
   954
        /* Mono -> Stereo [-> ...] */
slouken@1895
   955
        if ((src_channels == 1) && (dst_channels > 1)) {
slouken@11096
   956
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertMonoToStereo) < 0) {
slouken@11096
   957
                return -1;
slouken@11096
   958
            }
slouken@1895
   959
            cvt->len_mult *= 2;
slouken@1895
   960
            src_channels = 2;
slouken@1895
   961
            cvt->len_ratio *= 2;
slouken@1895
   962
        }
icculus@11405
   963
        /* [Mono ->] Stereo -> 5.1 [-> 7.1] */
icculus@11405
   964
        if ((src_channels == 2) && (dst_channels >= 6)) {
slouken@11096
   965
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertStereoTo51) < 0) {
slouken@11096
   966
                return -1;
slouken@11096
   967
            }
slouken@1895
   968
            src_channels = 6;
slouken@1895
   969
            cvt->len_mult *= 3;
slouken@1895
   970
            cvt->len_ratio *= 3;
slouken@1895
   971
        }
icculus@11405
   972
        /* Quad -> 5.1 [-> 7.1] */
icculus@11405
   973
        if ((src_channels == 4) && (dst_channels >= 6)) {
icculus@11405
   974
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertQuadTo51) < 0) {
icculus@11405
   975
                return -1;
icculus@11405
   976
            }
icculus@11405
   977
            src_channels = 6;
icculus@11405
   978
            cvt->len_mult = (cvt->len_mult * 3 + 1) / 2;
icculus@11405
   979
            cvt->len_ratio *= 1.5;
icculus@11405
   980
        }
icculus@11405
   981
        /* [[Mono ->] Stereo ->] 5.1 -> 7.1 */
icculus@11405
   982
        if ((src_channels == 6) && (dst_channels == 8)) {
icculus@11405
   983
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51To71) < 0) {
icculus@11405
   984
                return -1;
icculus@11405
   985
            }
icculus@11405
   986
            src_channels = 8;
icculus@11405
   987
            cvt->len_mult = (cvt->len_mult * 4 + 2) / 3;
icculus@11405
   988
            /* Should be numerically exact with every valid input to this
icculus@11405
   989
               function */
icculus@11405
   990
            cvt->len_ratio = cvt->len_ratio * 4 / 3;
icculus@11405
   991
        }
icculus@11405
   992
        /* [Mono ->] Stereo -> Quad */
slouken@1895
   993
        if ((src_channels == 2) && (dst_channels == 4)) {
slouken@11096
   994
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertStereoToQuad) < 0) {
slouken@11096
   995
                return -1;
slouken@11096
   996
            }
slouken@1895
   997
            src_channels = 4;
slouken@1895
   998
            cvt->len_mult *= 2;
slouken@1895
   999
            cvt->len_ratio *= 2;
slouken@1895
  1000
        }
icculus@11405
  1001
    } else if (src_channels > dst_channels) {
icculus@11405
  1002
        /* Downmixing */
icculus@11405
  1003
        /* 7.1 -> 5.1 [-> Stereo [-> Mono]] */
icculus@11405
  1004
        /* 7.1 -> 5.1 [-> Quad] */
icculus@11405
  1005
        if ((src_channels == 8) && (dst_channels <= 6)) {
icculus@11405
  1006
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert71To51) < 0) {
slouken@11096
  1007
                return -1;
slouken@11096
  1008
            }
icculus@11405
  1009
            src_channels = 6;
icculus@11405
  1010
            cvt->len_ratio *= 0.75;
slouken@1895
  1011
        }
icculus@11405
  1012
        /* [7.1 ->] 5.1 -> Stereo [-> Mono] */
slouken@1895
  1013
        if ((src_channels == 6) && (dst_channels <= 2)) {
slouken@11096
  1014
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51ToStereo) < 0) {
slouken@11096
  1015
                return -1;
slouken@11096
  1016
            }
slouken@1895
  1017
            src_channels = 2;
slouken@1895
  1018
            cvt->len_ratio /= 3;
slouken@1895
  1019
        }
icculus@11405
  1020
        /* 5.1 -> Quad */
slouken@1895
  1021
        if ((src_channels == 6) && (dst_channels == 4)) {
slouken@11096
  1022
            if (SDL_AddAudioCVTFilter(cvt, SDL_Convert51ToQuad) < 0) {
slouken@11096
  1023
                return -1;
slouken@11096
  1024
            }
slouken@1895
  1025
            src_channels = 4;
icculus@11405
  1026
            cvt->len_ratio = cvt->len_ratio * 2 / 3;
icculus@11405
  1027
        }
icculus@11405
  1028
        /* Quad -> Stereo [-> Mono] */
icculus@11405
  1029
        if ((src_channels == 4) && (dst_channels <= 2)) {
icculus@11405
  1030
            if (SDL_AddAudioCVTFilter(cvt, SDL_ConvertQuadToStereo) < 0) {
icculus@11405
  1031
                return -1;
icculus@11405
  1032
            }
icculus@11405
  1033
            src_channels = 2;
slouken@1895
  1034
            cvt->len_ratio /= 2;
slouken@1895
  1035
        }
icculus@11405
  1036
        /* [... ->] Stereo -> Mono */
icculus@11405
  1037
        if ((src_channels == 2) && (dst_channels == 1)) {
icculus@10832
  1038
            SDL_AudioFilter filter = NULL;
icculus@10832
  1039
icculus@10832
  1040
            #if HAVE_SSE3_INTRINSICS
icculus@10832
  1041
            if (SDL_HasSSE3()) {
icculus@10832
  1042
                filter = SDL_ConvertStereoToMono_SSE3;
icculus@10832
  1043
            }
icculus@10832
  1044
            #endif
icculus@10832
  1045
icculus@10832
  1046
            if (!filter) {
icculus@10832
  1047
                filter = SDL_ConvertStereoToMono;
icculus@10832
  1048
            }
icculus@10832
  1049
slouken@11096
  1050
            if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
slouken@11096
  1051
                return -1;
slouken@11096
  1052
            }
icculus@10832
  1053
icculus@11405
  1054
            src_channels = 1;
slouken@1895
  1055
            cvt->len_ratio /= 2;
slouken@1895
  1056
        }
slouken@1895
  1057
    }
slouken@0
  1058
icculus@11405
  1059
    if (src_channels != dst_channels) {
icculus@11405
  1060
        /* All combinations of supported channel counts should have been
icculus@11405
  1061
           handled by now, but let's be defensive */
icculus@11405
  1062
      return SDL_SetError("Invalid channel combination");
icculus@11405
  1063
    }
icculus@11405
  1064
    
icculus@3021
  1065
    /* Do rate conversion, if necessary. Updates (cvt). */
slouken@10767
  1066
    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) {
icculus@3021
  1067
        return -1;              /* shouldn't happen, but just in case... */
slouken@2716
  1068
    }
slouken@2716
  1069
icculus@10756
  1070
    /* Move to final data type. */
slouken@10767
  1071
    if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) < 0) {
icculus@10575
  1072
        return -1;              /* shouldn't happen, but just in case... */
slouken@1895
  1073
    }
icculus@10575
  1074
icculus@10575
  1075
    cvt->needed = (cvt->filter_index != 0);
slouken@1895
  1076
    return (cvt->needed);
slouken@0
  1077
}
slouken@1895
  1078
icculus@10842
  1079
typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const void *inbuf, const int inbuflen, void *outbuf, const int outbuflen);
slouken@10773
  1080
typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream);
slouken@10773
  1081
typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream);
icculus@10757
  1082
slouken@11631
  1083
struct _SDL_AudioStream
icculus@10757
  1084
{
icculus@10757
  1085
    SDL_AudioCVT cvt_before_resampling;
icculus@10757
  1086
    SDL_AudioCVT cvt_after_resampling;
icculus@10757
  1087
    SDL_DataQueue *queue;
icculus@11583
  1088
    SDL_bool first_run;
slouken@11632
  1089
    Uint8 *staging_buffer;
slouken@11632
  1090
    int staging_buffer_size;
slouken@11632
  1091
    int staging_buffer_filled;
icculus@10844
  1092
    Uint8 *work_buffer_base;  /* maybe unaligned pointer from SDL_realloc(). */
icculus@10757
  1093
    int work_buffer_len;
icculus@10757
  1094
    int src_sample_frame_size;
icculus@10757
  1095
    SDL_AudioFormat src_format;
icculus@10757
  1096
    Uint8 src_channels;
icculus@10757
  1097
    int src_rate;
icculus@10757
  1098
    int dst_sample_frame_size;
icculus@10757
  1099
    SDL_AudioFormat dst_format;
icculus@10757
  1100
    Uint8 dst_channels;
icculus@10757
  1101
    int dst_rate;
icculus@10757
  1102
    double rate_incr;
icculus@10757
  1103
    Uint8 pre_resample_channels;
slouken@10773
  1104
    int packetlen;
icculus@11583
  1105
    int resampler_padding_samples;
icculus@11583
  1106
    float *resampler_padding;
slouken@10773
  1107
    void *resampler_state;
slouken@10773
  1108
    SDL_ResampleAudioStreamFunc resampler_func;
slouken@10773
  1109
    SDL_ResetAudioStreamResamplerFunc reset_resampler_func;
slouken@10773
  1110
    SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func;
slouken@10773
  1111
};
slouken@10773
  1112
icculus@10851
  1113
static Uint8 *
icculus@10851
  1114
EnsureStreamBufferSize(SDL_AudioStream *stream, const int newlen)
icculus@10851
  1115
{
icculus@10851
  1116
    Uint8 *ptr;
icculus@10851
  1117
    size_t offset;
icculus@10851
  1118
icculus@10851
  1119
    if (stream->work_buffer_len >= newlen) {
icculus@10851
  1120
        ptr = stream->work_buffer_base;
icculus@10851
  1121
    } else {
icculus@10851
  1122
        ptr = (Uint8 *) SDL_realloc(stream->work_buffer_base, newlen + 32);
icculus@10851
  1123
        if (!ptr) {
icculus@10851
  1124
            SDL_OutOfMemory();
icculus@10851
  1125
            return NULL;
icculus@10851
  1126
        }
icculus@10851
  1127
        /* Make sure we're aligned to 16 bytes for SIMD code. */
icculus@10851
  1128
        stream->work_buffer_base = ptr;
icculus@10851
  1129
        stream->work_buffer_len = newlen;
icculus@10851
  1130
    }
icculus@10851
  1131
icculus@10851
  1132
    offset = ((size_t) ptr) & 15;
icculus@10851
  1133
    return offset ? ptr + (16 - offset) : ptr;
icculus@10851
  1134
}
icculus@10851
  1135
slouken@10777
  1136
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
  1137
static int
icculus@10842
  1138
SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen)
slouken@10773
  1139
{
icculus@10842
  1140
    const float *inbuf = (const float *) _inbuf;
icculus@10842
  1141
    float *outbuf = (float *) _outbuf;
icculus@10799
  1142
    const int framelen = sizeof(float) * stream->pre_resample_channels;
icculus@10790
  1143
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
  1144
    SRC_DATA data;
slouken@10773
  1145
    int result;
slouken@10773
  1146
icculus@11583
  1147
    SDL_assert(inbuf != ((const float *) outbuf));  /* SDL_AudioStreamPut() shouldn't allow in-place resamples. */
icculus@10851
  1148
slouken@10777
  1149
    data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
icculus@10799
  1150
    data.input_frames = inbuflen / framelen;
slouken@10773
  1151
    data.input_frames_used = 0;
slouken@10773
  1152
slouken@10773
  1153
    data.data_out = outbuf;
icculus@10799
  1154
    data.output_frames = outbuflen / framelen;
slouken@10773
  1155
slouken@10773
  1156
    data.end_of_input = 0;
slouken@10773
  1157
    data.src_ratio = stream->rate_incr;
slouken@10773
  1158
icculus@10790
  1159
    result = SRC_src_process(state, &data);
slouken@10773
  1160
    if (result != 0) {
icculus@10790
  1161
        SDL_SetError("src_process() failed: %s", SRC_src_strerror(result));
slouken@10773
  1162
        return 0;
slouken@10773
  1163
    }
slouken@10773
  1164
slouken@10773
  1165
    /* If this fails, we need to store them off somewhere */
slouken@10773
  1166
    SDL_assert(data.input_frames_used == data.input_frames);
slouken@10773
  1167
slouken@10773
  1168
    return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels);
slouken@10773
  1169
}
slouken@10773
  1170
slouken@10773
  1171
static void
slouken@10773
  1172
SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
  1173
{
icculus@10790
  1174
    SRC_src_reset((SRC_STATE *)stream->resampler_state);
slouken@10773
  1175
}
slouken@10773
  1176
slouken@10773
  1177
static void
slouken@10773
  1178
SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream)
slouken@10773
  1179
{
icculus@10790
  1180
    SRC_STATE *state = (SRC_STATE *)stream->resampler_state;
slouken@10773
  1181
    if (state) {
icculus@10790
  1182
        SRC_src_delete(state);
slouken@10773
  1183
    }
slouken@10773
  1184
slouken@10773
  1185
    stream->resampler_state = NULL;
slouken@10773
  1186
    stream->resampler_func = NULL;
slouken@10773
  1187
    stream->reset_resampler_func = NULL;
slouken@10773
  1188
    stream->cleanup_resampler_func = NULL;
slouken@10773
  1189
}
slouken@10773
  1190
slouken@10773
  1191
static SDL_bool
slouken@10773
  1192
SetupLibSampleRateResampling(SDL_AudioStream *stream)
slouken@10773
  1193
{
icculus@10790
  1194
    int result = 0;
icculus@10790
  1195
    SRC_STATE *state = NULL;
slouken@10773
  1196
icculus@10790
  1197
    if (SRC_available) {
icculus@10849
  1198
        state = SRC_src_new(SRC_converter, stream->pre_resample_channels, &result);
icculus@10790
  1199
        if (!state) {
icculus@10790
  1200
            SDL_SetError("src_new() failed: %s", SRC_src_strerror(result));
icculus@10790
  1201
        }
slouken@10773
  1202
    }
slouken@10773
  1203
icculus@10790
  1204
    if (!state) {
icculus@10790
  1205
        SDL_CleanupAudioStreamResampler_SRC(stream);
slouken@10773
  1206
        return SDL_FALSE;
slouken@10773
  1207
    }
slouken@10773
  1208
slouken@10773
  1209
    stream->resampler_state = state;
slouken@10773
  1210
    stream->resampler_func = SDL_ResampleAudioStream_SRC;
slouken@10773
  1211
    stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC;
slouken@10773
  1212
    stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC;
slouken@10773
  1213
slouken@10773
  1214
    return SDL_TRUE;
slouken@10773
  1215
}
icculus@10790
  1216
#endif /* HAVE_LIBSAMPLERATE_H */
slouken@10773
  1217
slouken@10773
  1218
slouken@10773
  1219
static int
icculus@10842
  1220
SDL_ResampleAudioStream(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen)
slouken@10773
  1221
{
icculus@11583
  1222
    const Uint8 *inbufend = ((const Uint8 *) _inbuf) + inbuflen;
icculus@10842
  1223
    const float *inbuf = (const float *) _inbuf;
icculus@10842
  1224
    float *outbuf = (float *) _outbuf;
icculus@11517
  1225
    const int chans = (int) stream->pre_resample_channels;
icculus@11517
  1226
    const int inrate = stream->src_rate;
icculus@11517
  1227
    const int outrate = stream->dst_rate;
icculus@11583
  1228
    const int paddingsamples = stream->resampler_padding_samples;
icculus@11517
  1229
    const int paddingbytes = paddingsamples * sizeof (float);
icculus@11517
  1230
    float *lpadding = (float *) stream->resampler_state;
icculus@11583
  1231
    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
  1232
    const int cpy = SDL_min(inbuflen, paddingbytes);
icculus@11517
  1233
    int retval;
slouken@10773
  1234
icculus@11583
  1235
    SDL_assert(inbuf != ((const float *) outbuf));  /* SDL_AudioStreamPut() shouldn't allow in-place resamples. */
slouken@11519
  1236
icculus@11517
  1237
    retval = SDL_ResampleAudio(chans, inrate, outrate, lpadding, rpadding, inbuf, inbuflen, outbuf, outbuflen);
slouken@10773
  1238
icculus@11517
  1239
    /* update our left padding with end of current input, for next run. */
icculus@11591
  1240
    SDL_memcpy((lpadding + paddingsamples) - (cpy / sizeof (float)), inbufend - cpy, cpy);
icculus@11517
  1241
    return retval;
slouken@10773
  1242
}
slouken@10773
  1243
slouken@10773
  1244
static void
slouken@10773
  1245
SDL_ResetAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
  1246
{
icculus@11583
  1247
    /* set all the padding to silence. */
icculus@11583
  1248
    const int len = stream->resampler_padding_samples;
icculus@11517
  1249
    SDL_memset(stream->resampler_state, '\0', len * sizeof (float));
slouken@10773
  1250
}
slouken@10773
  1251
slouken@10773
  1252
static void
slouken@10773
  1253
SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream)
slouken@10773
  1254
{
slouken@10773
  1255
    SDL_free(stream->resampler_state);
slouken@10773
  1256
}
icculus@10757
  1257
icculus@10789
  1258
SDL_AudioStream *
icculus@10789
  1259
SDL_NewAudioStream(const SDL_AudioFormat src_format,
icculus@10789
  1260
                   const Uint8 src_channels,
icculus@10789
  1261
                   const int src_rate,
icculus@10789
  1262
                   const SDL_AudioFormat dst_format,
icculus@10789
  1263
                   const Uint8 dst_channels,
icculus@10789
  1264
                   const int dst_rate)
icculus@10757
  1265
{
icculus@10757
  1266
    const int packetlen = 4096;  /* !!! FIXME: good enough for now. */
icculus@10757
  1267
    Uint8 pre_resample_channels;
icculus@10757
  1268
    SDL_AudioStream *retval;
icculus@10757
  1269
icculus@10757
  1270
    retval = (SDL_AudioStream *) SDL_calloc(1, sizeof (SDL_AudioStream));
icculus@10757
  1271
    if (!retval) {
icculus@10757
  1272
        return NULL;
icculus@10757
  1273
    }
icculus@10757
  1274
icculus@10757
  1275
    /* If increasing channels, do it after resampling, since we'd just
icculus@10757
  1276
       do more work to resample duplicate channels. If we're decreasing, do
icculus@10757
  1277
       it first so we resample the interpolated data instead of interpolating
icculus@10757
  1278
       the resampled data (!!! FIXME: decide if that works in practice, though!). */
icculus@10757
  1279
    pre_resample_channels = SDL_min(src_channels, dst_channels);
icculus@10757
  1280
icculus@11583
  1281
    retval->first_run = SDL_TRUE;
icculus@10883
  1282
    retval->src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels;
icculus@10757
  1283
    retval->src_format = src_format;
icculus@10757
  1284
    retval->src_channels = src_channels;
icculus@10757
  1285
    retval->src_rate = src_rate;
icculus@10883
  1286
    retval->dst_sample_frame_size = (SDL_AUDIO_BITSIZE(dst_format) / 8) * dst_channels;
icculus@10757
  1287
    retval->dst_format = dst_format;
icculus@10757
  1288
    retval->dst_channels = dst_channels;
icculus@10757
  1289
    retval->dst_rate = dst_rate;
icculus@10757
  1290
    retval->pre_resample_channels = pre_resample_channels;
icculus@10757
  1291
    retval->packetlen = packetlen;
icculus@10757
  1292
    retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
icculus@11583
  1293
    retval->resampler_padding_samples = ResamplerPadding(retval->src_rate, retval->dst_rate) * pre_resample_channels;
icculus@11583
  1294
    retval->resampler_padding = (float *) SDL_calloc(retval->resampler_padding_samples, sizeof (float));
icculus@11583
  1295
icculus@11583
  1296
    if (retval->resampler_padding == NULL) {
icculus@11583
  1297
        SDL_FreeAudioStream(retval);
icculus@11583
  1298
        SDL_OutOfMemory();
icculus@11583
  1299
        return NULL;
icculus@11583
  1300
    }
icculus@10757
  1301
slouken@11632
  1302
    retval->staging_buffer_size = ((retval->resampler_padding_samples / retval->pre_resample_channels) * retval->src_sample_frame_size);
slouken@11632
  1303
    if (retval->staging_buffer_size > 0) {
slouken@11632
  1304
        retval->staging_buffer = (Uint8 *) SDL_malloc(retval->staging_buffer_size);
icculus@11634
  1305
        if (retval->staging_buffer == NULL) {
slouken@11632
  1306
            SDL_FreeAudioStream(retval);
slouken@11632
  1307
            SDL_OutOfMemory();
slouken@11632
  1308
            return NULL;
slouken@11632
  1309
        }
slouken@11632
  1310
    }
slouken@11632
  1311
slouken@11632
  1312
    /* Not resampling? It's an easy conversion (and maybe not even that!) */
icculus@10757
  1313
    if (src_rate == dst_rate) {
icculus@10757
  1314
        retval->cvt_before_resampling.needed = SDL_FALSE;
slouken@10773
  1315
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
  1316
            SDL_FreeAudioStream(retval);
icculus@10757
  1317
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1318
        }
icculus@10757
  1319
    } else {
icculus@10757
  1320
        /* Don't resample at first. Just get us to Float32 format. */
icculus@10757
  1321
        /* !!! FIXME: convert to int32 on devices without hardware float. */
slouken@10773
  1322
        if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) {
slouken@10773
  1323
            SDL_FreeAudioStream(retval);
icculus@10757
  1324
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1325
        }
icculus@10757
  1326
slouken@10777
  1327
#ifdef HAVE_LIBSAMPLERATE_H
slouken@10773
  1328
        SetupLibSampleRateResampling(retval);
slouken@10773
  1329
#endif
slouken@10773
  1330
slouken@10773
  1331
        if (!retval->resampler_func) {
icculus@11583
  1332
            retval->resampler_state = SDL_calloc(retval->resampler_padding_samples, sizeof (float));
slouken@10773
  1333
            if (!retval->resampler_state) {
slouken@10773
  1334
                SDL_FreeAudioStream(retval);
slouken@10773
  1335
                SDL_OutOfMemory();
slouken@10773
  1336
                return NULL;
slouken@10773
  1337
            }
icculus@11508
  1338
icculus@11508
  1339
            if (SDL_PrepareResampleFilter() < 0) {
icculus@11508
  1340
                SDL_free(retval->resampler_state);
icculus@11508
  1341
                retval->resampler_state = NULL;
icculus@11508
  1342
                SDL_FreeAudioStream(retval);
icculus@11508
  1343
                return NULL;
icculus@11508
  1344
            }
icculus@11508
  1345
slouken@10773
  1346
            retval->resampler_func = SDL_ResampleAudioStream;
slouken@10773
  1347
            retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
slouken@10773
  1348
            retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
slouken@10773
  1349
        }
slouken@10773
  1350
icculus@10757
  1351
        /* Convert us to the final format after resampling. */
slouken@10773
  1352
        if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) {
slouken@10773
  1353
            SDL_FreeAudioStream(retval);
icculus@10757
  1354
            return NULL;  /* SDL_BuildAudioCVT should have called SDL_SetError. */
icculus@10757
  1355
        }
icculus@10757
  1356
    }
icculus@10757
  1357
icculus@10757
  1358
    retval->queue = SDL_NewDataQueue(packetlen, packetlen * 2);
icculus@10757
  1359
    if (!retval->queue) {
slouken@10773
  1360
        SDL_FreeAudioStream(retval);
icculus@10757
  1361
        return NULL;  /* SDL_NewDataQueue should have called SDL_SetError. */
icculus@10757
  1362
    }
icculus@10757
  1363
icculus@10757
  1364
    return retval;
icculus@10757
  1365
}
icculus@10757
  1366
slouken@11632
  1367
static int
icculus@11636
  1368
SDL_AudioStreamPutInternal(SDL_AudioStream *stream, const void *buf, int len, int *maxputbytes)
icculus@10757
  1369
{
slouken@11631
  1370
    int buflen = len;
icculus@11583
  1371
    int workbuflen;
icculus@11583
  1372
    Uint8 *workbuf;
icculus@11583
  1373
    Uint8 *resamplebuf = NULL;
icculus@11583
  1374
    int resamplebuflen = 0;
icculus@11590
  1375
    int neededpaddingbytes;
icculus@11583
  1376
    int paddingbytes;
icculus@10757
  1377
icculus@10844
  1378
    /* !!! FIXME: several converters can take advantage of SIMD, but only
icculus@10844
  1379
       !!! FIXME:  if the data is aligned to 16 bytes. EnsureStreamBufferSize()
icculus@10844
  1380
       !!! FIXME:  guarantees the buffer will align, but the
icculus@10844
  1381
       !!! FIXME:  converters will iterate over the data backwards if
icculus@10844
  1382
       !!! FIXME:  the output grows, and this means we won't align if buflen
icculus@10844
  1383
       !!! FIXME:  isn't a multiple of 16. In these cases, we should chop off
icculus@10844
  1384
       !!! FIXME:  a few samples at the end and convert them separately. */
icculus@10844
  1385
icculus@11583
  1386
    /* no padding prepended on first run. */
icculus@11590
  1387
    neededpaddingbytes = stream->resampler_padding_samples * sizeof (float);
icculus@11583
  1388
    paddingbytes = stream->first_run ? 0 : neededpaddingbytes;
icculus@11583
  1389
    stream->first_run = SDL_FALSE;
icculus@11583
  1390
icculus@11583
  1391
    /* Make sure the work buffer can hold all the data we need at once... */
icculus@11583
  1392
    workbuflen = buflen;
icculus@10757
  1393
    if (stream->cvt_before_resampling.needed) {
icculus@11583
  1394
        workbuflen *= stream->cvt_before_resampling.len_mult;
icculus@11583
  1395
    }
icculus@11583
  1396
icculus@11583
  1397
    if (stream->dst_rate != stream->src_rate) {
icculus@11583
  1398
        /* resamples can't happen in place, so make space for second buf. */
icculus@11583
  1399
        const int framesize = stream->pre_resample_channels * sizeof (float);
icculus@11583
  1400
        const int frames = workbuflen / framesize;
icculus@11583
  1401
        resamplebuflen = ((int) SDL_ceil(frames * stream->rate_incr)) * framesize;
icculus@11583
  1402
        #if DEBUG_AUDIOSTREAM
icculus@11583
  1403
        printf("AUDIOSTREAM: will resample %d bytes to %d (ratio=%.6f)\n", workbuflen, resamplebuflen, stream->rate_incr);
icculus@11583
  1404
        #endif
icculus@11583
  1405
        workbuflen += resamplebuflen;
icculus@11583
  1406
    }
icculus@11583
  1407
icculus@11583
  1408
    if (stream->cvt_after_resampling.needed) {
icculus@11583
  1409
        /* !!! FIXME: buffer might be big enough already? */
icculus@11583
  1410
        workbuflen *= stream->cvt_after_resampling.len_mult;
icculus@11583
  1411
    }
icculus@11583
  1412
icculus@11583
  1413
    workbuflen += neededpaddingbytes;
icculus@11583
  1414
icculus@11583
  1415
    #if DEBUG_AUDIOSTREAM
icculus@11583
  1416
    printf("AUDIOSTREAM: Putting %d bytes of preconverted audio, need %d byte work buffer\n", buflen, workbuflen);
icculus@11583
  1417
    #endif
icculus@11583
  1418
icculus@11583
  1419
    workbuf = EnsureStreamBufferSize(stream, workbuflen);
icculus@11583
  1420
    if (!workbuf) {
icculus@11583
  1421
        return -1;  /* probably out of memory. */
icculus@11583
  1422
    }
icculus@11583
  1423
icculus@11583
  1424
    resamplebuf = workbuf;  /* default if not resampling. */
icculus@11583
  1425
icculus@11583
  1426
    SDL_memcpy(workbuf + paddingbytes, buf, buflen);
icculus@11583
  1427
icculus@11583
  1428
    if (stream->cvt_before_resampling.needed) {
icculus@11583
  1429
        stream->cvt_before_resampling.buf = workbuf + paddingbytes;
icculus@10757
  1430
        stream->cvt_before_resampling.len = buflen;
icculus@10757
  1431
        if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) {
icculus@10757
  1432
            return -1;   /* uhoh! */
icculus@10757
  1433
        }
icculus@10757
  1434
        buflen = stream->cvt_before_resampling.len_cvt;
icculus@11583
  1435
icculus@11583
  1436
        #if DEBUG_AUDIOSTREAM
icculus@11583
  1437
        printf("AUDIOSTREAM: After initial conversion we have %d bytes\n", buflen);
icculus@11583
  1438
        #endif
icculus@10757
  1439
    }
icculus@10757
  1440
icculus@10757
  1441
    if (stream->dst_rate != stream->src_rate) {
icculus@11583
  1442
        /* save off some samples at the end; they are used for padding now so
icculus@11583
  1443
           the resampler is coherent and then used at the start of the next
icculus@11583
  1444
           put operation. Prepend last put operation's padding, too. */
icculus@11583
  1445
icculus@11583
  1446
        /* prepend prior put's padding. :P */
icculus@11583
  1447
        if (paddingbytes) {
icculus@11583
  1448
            SDL_memcpy(workbuf, stream->resampler_padding, paddingbytes);
icculus@11583
  1449
            buflen += paddingbytes;
icculus@10757
  1450
        }
icculus@11583
  1451
icculus@11583
  1452
        /* save off the data at the end for the next run. */
icculus@11583
  1453
        SDL_memcpy(stream->resampler_padding, workbuf + (buflen - neededpaddingbytes), neededpaddingbytes);
icculus@11583
  1454
icculus@11583
  1455
        resamplebuf = workbuf + buflen;  /* skip to second piece of workbuf. */
icculus@11591
  1456
        SDL_assert(buflen >= neededpaddingbytes);
icculus@11591
  1457
        if (buflen > neededpaddingbytes) {
icculus@11591
  1458
            buflen = stream->resampler_func(stream, workbuf, buflen - neededpaddingbytes, resamplebuf, resamplebuflen);
icculus@11591
  1459
        } else {
icculus@11591
  1460
            buflen = 0;
icculus@11591
  1461
        }
icculus@11583
  1462
icculus@11583
  1463
        #if DEBUG_AUDIOSTREAM
icculus@11583
  1464
        printf("AUDIOSTREAM: After resampling we have %d bytes\n", buflen);
icculus@11583
  1465
        #endif
icculus@10757
  1466
    }
icculus@10757
  1467
icculus@11591
  1468
    if (stream->cvt_after_resampling.needed && (buflen > 0)) {
icculus@11583
  1469
        stream->cvt_after_resampling.buf = resamplebuf;
icculus@10757
  1470
        stream->cvt_after_resampling.len = buflen;
icculus@10757
  1471
        if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) {
icculus@10757
  1472
            return -1;   /* uhoh! */
icculus@10757
  1473
        }
icculus@10757
  1474
        buflen = stream->cvt_after_resampling.len_cvt;
icculus@11583
  1475
icculus@11583
  1476
        #if DEBUG_AUDIOSTREAM
icculus@11583
  1477
        printf("AUDIOSTREAM: After final conversion we have %d bytes\n", buflen);
icculus@11583
  1478
        #endif
icculus@10757
  1479
    }
icculus@10757
  1480
icculus@11583
  1481
    #if DEBUG_AUDIOSTREAM
icculus@11583
  1482
    printf("AUDIOSTREAM: Final output is %d bytes\n", buflen);
icculus@11583
  1483
    #endif
icculus@11583
  1484
icculus@11636
  1485
    if (maxputbytes) {
icculus@11636
  1486
        const int maxbytes = *maxputbytes;
icculus@11636
  1487
        if (buflen > maxbytes)
icculus@11636
  1488
            buflen = maxbytes;
icculus@11636
  1489
        *maxputbytes -= buflen;
icculus@11636
  1490
    }
icculus@11636
  1491
icculus@11583
  1492
    /* resamplebuf holds the final output, even if we didn't resample. */
icculus@11591
  1493
    return buflen ? SDL_WriteToDataQueue(stream->queue, resamplebuf, buflen) : 0;
icculus@10757
  1494
}
icculus@10757
  1495
slouken@11632
  1496
int
slouken@11632
  1497
SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, int len)
slouken@11632
  1498
{
slouken@11632
  1499
    /* !!! FIXME: several converters can take advantage of SIMD, but only
slouken@11632
  1500
       !!! FIXME:  if the data is aligned to 16 bytes. EnsureStreamBufferSize()
slouken@11632
  1501
       !!! FIXME:  guarantees the buffer will align, but the
slouken@11632
  1502
       !!! FIXME:  converters will iterate over the data backwards if
slouken@11632
  1503
       !!! FIXME:  the output grows, and this means we won't align if buflen
slouken@11632
  1504
       !!! FIXME:  isn't a multiple of 16. In these cases, we should chop off
slouken@11632
  1505
       !!! FIXME:  a few samples at the end and convert them separately. */
slouken@11632
  1506
slouken@11632
  1507
    #if DEBUG_AUDIOSTREAM
slouken@11632
  1508
    printf("AUDIOSTREAM: wants to put %d preconverted bytes\n", buflen);
slouken@11632
  1509
    #endif
slouken@11632
  1510
slouken@11632
  1511
    if (!stream) {
slouken@11632
  1512
        return SDL_InvalidParamError("stream");
slouken@11632
  1513
    } else if (!buf) {
slouken@11632
  1514
        return SDL_InvalidParamError("buf");
slouken@11632
  1515
    } else if (len == 0) {
slouken@11632
  1516
        return 0;  /* nothing to do. */
slouken@11632
  1517
    } else if ((len % stream->src_sample_frame_size) != 0) {
slouken@11632
  1518
        return SDL_SetError("Can't add partial sample frames");
slouken@11632
  1519
    }
slouken@11632
  1520
slouken@11632
  1521
    if (!stream->cvt_before_resampling.needed &&
slouken@11632
  1522
        (stream->dst_rate == stream->src_rate) &&
slouken@11632
  1523
        !stream->cvt_after_resampling.needed) {
slouken@11632
  1524
        #if DEBUG_AUDIOSTREAM
slouken@11632
  1525
        printf("AUDIOSTREAM: no conversion needed at all, queueing %d bytes.\n", len);
slouken@11632
  1526
        #endif
slouken@11632
  1527
        return SDL_WriteToDataQueue(stream->queue, buf, len);
slouken@11632
  1528
    }
slouken@11632
  1529
slouken@11632
  1530
    while (len > 0) {
slouken@11632
  1531
        int amount;
slouken@11632
  1532
slouken@11632
  1533
        /* If we don't have a staging buffer or we're given enough data that
slouken@11632
  1534
           we don't need to store it for later, skip the staging process.
slouken@11632
  1535
         */
slouken@11632
  1536
        if (!stream->staging_buffer_filled && len >= stream->staging_buffer_size) {
icculus@11636
  1537
            return SDL_AudioStreamPutInternal(stream, buf, len, NULL);
slouken@11632
  1538
        }
slouken@11632
  1539
slouken@11632
  1540
        /* If there's not enough data to fill the staging buffer, just save it */
slouken@11632
  1541
        if ((stream->staging_buffer_filled + len) < stream->staging_buffer_size) {
slouken@11632
  1542
            SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, len);
slouken@11632
  1543
            stream->staging_buffer_filled += len;
slouken@11632
  1544
            return 0;
slouken@11632
  1545
        }
slouken@11632
  1546
 
slouken@11632
  1547
        /* Fill the staging buffer, process it, and continue */
slouken@11632
  1548
        amount = (stream->staging_buffer_size - stream->staging_buffer_filled);
slouken@11632
  1549
        SDL_assert(amount > 0);
slouken@11632
  1550
        SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, amount);
slouken@11632
  1551
        stream->staging_buffer_filled = 0;
icculus@11636
  1552
        if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, NULL) < 0) {
slouken@11632
  1553
            return -1;
slouken@11632
  1554
        }
slouken@11632
  1555
        buf = (void *)((Uint8 *)buf + amount);
slouken@11632
  1556
        len -= amount;
slouken@11632
  1557
    }
slouken@11632
  1558
    return 0;
slouken@11632
  1559
}
slouken@11632
  1560
icculus@11636
  1561
int SDL_AudioStreamFlush(SDL_AudioStream *stream)
icculus@11636
  1562
{
icculus@11636
  1563
    if (!stream) {
icculus@11636
  1564
        return SDL_InvalidParamError("stream");
icculus@11636
  1565
    }
icculus@11636
  1566
icculus@11636
  1567
    #if DEBUG_AUDIOSTREAM
icculus@11636
  1568
    printf("AUDIOSTREAM: flushing! staging_buffer_filled=%d bytes\n", stream->staging_buffer_filled);
icculus@11636
  1569
    #endif
icculus@11636
  1570
icculus@11636
  1571
    /* shouldn't use a staging buffer if we're not resampling. */
icculus@11636
  1572
    SDL_assert((stream->dst_rate != stream->src_rate) || (stream->staging_buffer_filled == 0));
icculus@11636
  1573
icculus@11636
  1574
    if (stream->staging_buffer_filled > 0) {
icculus@11636
  1575
        /* push the staging buffer + silence. We need to flush out not just
icculus@11636
  1576
           the staging buffer, but the piece that the stream was saving off
icculus@11636
  1577
           for right-side resampler padding. */
icculus@11636
  1578
        const SDL_bool first_run = stream->first_run;
icculus@11636
  1579
        const int filled = stream->staging_buffer_filled;
icculus@11636
  1580
        int actual_input_frames = filled / stream->src_sample_frame_size;
icculus@11636
  1581
        if (!first_run)
icculus@11636
  1582
            actual_input_frames += stream->resampler_padding_samples / stream->pre_resample_channels;
icculus@11636
  1583
icculus@11636
  1584
        if (actual_input_frames > 0) {  /* don't bother if nothing to flush. */
icculus@11636
  1585
            /* This is how many bytes we're expecting without silence appended. */
icculus@11636
  1586
            int flush_remaining = ((int) SDL_ceil(actual_input_frames * stream->rate_incr)) * stream->dst_sample_frame_size;
icculus@11636
  1587
icculus@11636
  1588
            #if DEBUG_AUDIOSTREAM
icculus@11636
  1589
            printf("AUDIOSTREAM: flushing with padding to get max %d bytes!\n", flush_remaining);
icculus@11636
  1590
            #endif
icculus@11636
  1591
icculus@11636
  1592
            SDL_memset(stream->staging_buffer + filled, '\0', stream->staging_buffer_size - filled);
icculus@11636
  1593
            if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, &flush_remaining) < 0) {
icculus@11636
  1594
                return -1;
icculus@11636
  1595
            }
icculus@11636
  1596
icculus@11636
  1597
            /* we have flushed out (or initially filled) the pending right-side
icculus@11636
  1598
               resampler padding, but we need to push more silence to guarantee
icculus@11636
  1599
               the staging buffer is fully flushed out, too. */
icculus@11636
  1600
            SDL_memset(stream->staging_buffer, '\0', filled);
icculus@11636
  1601
            if (SDL_AudioStreamPutInternal(stream, stream->staging_buffer, stream->staging_buffer_size, &flush_remaining) < 0) {
icculus@11636
  1602
                return -1;
icculus@11636
  1603
            }
icculus@11636
  1604
        }
icculus@11636
  1605
    }
icculus@11636
  1606
icculus@11636
  1607
    stream->staging_buffer_filled = 0;
icculus@11636
  1608
    stream->first_run = SDL_TRUE;
icculus@11636
  1609
icculus@11636
  1610
    return 0;
icculus@11636
  1611
}
icculus@11636
  1612
icculus@10757
  1613
/* get converted/resampled data from the stream */
icculus@10757
  1614
int
slouken@11631
  1615
SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, int len)
icculus@10757
  1616
{
icculus@11583
  1617
    #if DEBUG_AUDIOSTREAM
slouken@11631
  1618
    printf("AUDIOSTREAM: want to get %d converted bytes\n", len);
icculus@11583
  1619
    #endif
icculus@11583
  1620
icculus@10757
  1621
    if (!stream) {
icculus@10757
  1622
        return SDL_InvalidParamError("stream");
icculus@10757
  1623
    } else if (!buf) {
icculus@10757
  1624
        return SDL_InvalidParamError("buf");
slouken@11631
  1625
    } else if (len <= 0) {
icculus@10757
  1626
        return 0;  /* nothing to do. */
icculus@10757
  1627
    } else if ((len % stream->dst_sample_frame_size) != 0) {
icculus@10757
  1628
        return SDL_SetError("Can't request partial sample frames");
icculus@10757
  1629
    }
icculus@10757
  1630
icculus@10764
  1631
    return (int) SDL_ReadFromDataQueue(stream->queue, buf, len);
icculus@10757
  1632
}
icculus@10757
  1633
icculus@10757
  1634
/* number of converted/resampled bytes available */
icculus@10757
  1635
int
icculus@10757
  1636
SDL_AudioStreamAvailable(SDL_AudioStream *stream)
icculus@10757
  1637
{
icculus@10757
  1638
    return stream ? (int) SDL_CountDataQueue(stream->queue) : 0;
icculus@10757
  1639
}
icculus@10757
  1640
slouken@11631
  1641
void
slouken@11631
  1642
SDL_AudioStreamClear(SDL_AudioStream *stream)
slouken@11631
  1643
{
slouken@11631
  1644
    if (!stream) {
slouken@11631
  1645
        SDL_InvalidParamError("stream");
slouken@11631
  1646
    } else {
slouken@11631
  1647
        SDL_ClearDataQueue(stream->queue, stream->packetlen * 2);
slouken@11631
  1648
        if (stream->reset_resampler_func) {
slouken@11631
  1649
            stream->reset_resampler_func(stream);
slouken@11631
  1650
        }
slouken@11631
  1651
        stream->first_run = SDL_TRUE;
icculus@11636
  1652
        stream->staging_buffer_filled = 0;
slouken@11631
  1653
    }
slouken@11631
  1654
}
slouken@11631
  1655
icculus@10757
  1656
/* dispose of a stream */
icculus@10757
  1657
void
icculus@10757
  1658
SDL_FreeAudioStream(SDL_AudioStream *stream)
icculus@10757
  1659
{
icculus@10757
  1660
    if (stream) {
slouken@10773
  1661
        if (stream->cleanup_resampler_func) {
slouken@10773
  1662
            stream->cleanup_resampler_func(stream);
slouken@10773
  1663
        }
icculus@10757
  1664
        SDL_FreeDataQueue(stream->queue);
slouken@11632
  1665
        SDL_free(stream->staging_buffer);
icculus@10844
  1666
        SDL_free(stream->work_buffer_base);
icculus@11583
  1667
        SDL_free(stream->resampler_padding);
icculus@10757
  1668
        SDL_free(stream);
icculus@10757
  1669
    }
icculus@10757
  1670
}
icculus@10757
  1671
icculus@10575
  1672
/* vi: set ts=4 sw=4 expandtab: */
slouken@2716
  1673