2015-06-21 17:33:46 +02:00
/*
Simple DirectMedia Layer
2017-01-02 03:33:28 +01:00
Copyright ( C ) 1997 - 2017 Sam Lantinga < slouken @ libsdl . org >
2015-06-21 17:33:46 +02:00
This software is provided ' as - is ' , without any express or implied
warranty . In no event will the authors be held liable for any damages
arising from the use of this software .
Permission is granted to anyone to use this software for any purpose ,
including commercial applications , and to alter it and redistribute it
freely , subject to the following restrictions :
1. The origin of this software must not be misrepresented ; you must not
claim that you wrote the original software . If you use this software
in a product , an acknowledgment in the product documentation would be
appreciated but is not required .
2. Altered source versions must be plainly marked as such , and must not be
misrepresented as being the original software .
3. This notice may not be removed or altered from any source distribution .
*/
# include "../SDL_internal.h"
/* Functions for audio drivers to perform runtime conversion of audio format */
2017-08-18 22:52:19 +02:00
# include "SDL.h"
2015-06-21 17:33:46 +02:00
# include "SDL_audio.h"
# include "SDL_audio_c.h"
2017-01-06 11:16:26 +01:00
# include "SDL_loadso.h"
2015-06-21 17:33:46 +02:00
# include "SDL_assert.h"
2017-01-06 01:29:38 +01:00
# include "../SDL_dataqueue.h"
2017-01-23 06:57:19 +01:00
# include "SDL_cpuinfo.h"
2017-01-23 07:05:44 +01:00
# ifdef __SSE3__
# define HAVE_SSE3_INTRINSICS 1
2017-01-23 06:57:19 +01:00
# endif
# if HAVE_SSE3_INTRINSICS
2017-08-29 06:41:45 +02:00
/* Convert from stereo to mono. Average left and right. */
2017-01-23 06:57:19 +01:00
static void SDLCALL
SDL_ConvertStereoToMono_SSE3 ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
{
float * dst = ( float * ) cvt - > buf ;
const float * src = dst ;
int i = cvt - > len_cvt / 8 ;
LOG_DEBUG_CONVERT ( " stereo " , " mono (using SSE3) " ) ;
SDL_assert ( format = = AUDIO_F32SYS ) ;
/* We can only do this if dst is aligned to 16 bytes; since src is the
same pointer and it moves by 2 , it can ' t be forcibly aligned . */
if ( ( ( ( size_t ) dst ) & 15 ) = = 0 ) {
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
const __m128 divby2 = _mm_set1_ps ( 0.5f ) ;
while ( i > = 4 ) { /* 4 * float32 */
_mm_store_ps ( dst , _mm_mul_ps ( _mm_hadd_ps ( _mm_load_ps ( src ) , _mm_load_ps ( src + 4 ) ) , divby2 ) ) ;
i - = 4 ; src + = 8 ; dst + = 4 ;
}
}
/* Finish off any leftovers with scalar operations. */
while ( i ) {
* dst = ( src [ 0 ] + src [ 1 ] ) * 0.5f ;
dst + + ; i - - ; src + = 2 ;
}
cvt - > len_cvt / = 2 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
# endif
2017-08-29 06:41:45 +02:00
/* Convert from stereo to mono. Average left and right. */
2015-06-21 17:33:46 +02:00
static void SDLCALL
2017-01-08 22:18:49 +01:00
SDL_ConvertStereoToMono ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
2015-06-21 17:33:46 +02:00
{
2016-11-05 07:34:38 +01:00
float * dst = ( float * ) cvt - > buf ;
const float * src = dst ;
2015-06-21 17:33:46 +02:00
int i ;
2016-11-05 07:34:38 +01:00
LOG_DEBUG_CONVERT ( " stereo " , " mono " ) ;
SDL_assert ( format = = AUDIO_F32SYS ) ;
for ( i = cvt - > len_cvt / 8 ; i ; - - i , src + = 2 ) {
2017-01-23 02:18:59 +01:00
* ( dst + + ) = ( src [ 0 ] + src [ 1 ] ) * 0.5f ;
2015-06-21 17:33:46 +02:00
}
cvt - > len_cvt / = 2 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
2017-08-29 06:41:45 +02:00
/* Convert from 5.1 to stereo. Average left and right, distribute center, discard LFE. */
2015-06-21 17:33:46 +02:00
static void SDLCALL
2017-01-08 22:18:49 +01:00
SDL_Convert51ToStereo ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
2015-06-21 17:33:46 +02:00
{
2016-11-05 07:34:38 +01:00
float * dst = ( float * ) cvt - > buf ;
const float * src = dst ;
2015-06-21 17:33:46 +02:00
int i ;
2017-01-08 22:18:49 +01:00
LOG_DEBUG_CONVERT ( " 5.1 " , " stereo " ) ;
2016-11-05 07:34:38 +01:00
SDL_assert ( format = = AUDIO_F32SYS ) ;
2015-06-21 17:33:46 +02:00
2017-08-29 06:41:45 +02:00
/* SDL's 5.1 layout: FL+FR+FC+LFE+BL+BR */
2016-11-05 07:34:38 +01:00
for ( i = cvt - > len_cvt / ( sizeof ( float ) * 6 ) ; i ; - - i , src + = 6 , dst + = 2 ) {
2017-08-29 06:41:45 +02:00
const float front_center_distributed = src [ 2 ] * 0.5f ;
dst [ 0 ] = ( src [ 0 ] + front_center_distributed + src [ 4 ] ) / 2.5f ; /* left */
dst [ 1 ] = ( src [ 1 ] + front_center_distributed + src [ 5 ] ) / 2.5f ; /* right */
2015-06-21 17:33:46 +02:00
}
cvt - > len_cvt / = 3 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
2017-08-29 06:41:45 +02:00
/* Convert from quad to stereo. Average left and right. */
static void SDLCALL
SDL_ConvertQuadToStereo ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
{
float * dst = ( float * ) cvt - > buf ;
const float * src = dst ;
int i ;
LOG_DEBUG_CONVERT ( " quad " , " stereo " ) ;
SDL_assert ( format = = AUDIO_F32SYS ) ;
for ( i = cvt - > len_cvt / ( sizeof ( float ) * 4 ) ; i ; - - i , src + = 4 , dst + = 2 ) {
dst [ 0 ] = ( src [ 0 ] + src [ 2 ] ) * 0.5f ; /* left */
dst [ 1 ] = ( src [ 1 ] + src [ 3 ] ) * 0.5f ; /* right */
}
cvt - > len_cvt / = 3 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
/* Convert from 7.1 to 5.1. Distribute sides across front and back. */
static void SDLCALL
SDL_Convert71To51 ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
{
float * dst = ( float * ) cvt - > buf ;
const float * src = dst ;
int i ;
LOG_DEBUG_CONVERT ( " 7.1 " , " 5.1 " ) ;
SDL_assert ( format = = AUDIO_F32SYS ) ;
for ( i = cvt - > len_cvt / ( sizeof ( float ) * 8 ) ; i ; - - i , src + = 8 , dst + = 6 ) {
const float surround_left_distributed = src [ 6 ] * 0.5f ;
const float surround_right_distributed = src [ 7 ] * 0.5f ;
dst [ 0 ] = ( src [ 0 ] + surround_left_distributed ) / 1.5f ; /* FL */
dst [ 1 ] = ( src [ 1 ] + surround_right_distributed ) / 1.5f ; /* FR */
dst [ 2 ] = src [ 2 ] / 1.5f ; /* CC */
dst [ 3 ] = src [ 3 ] / 1.5f ; /* LFE */
dst [ 4 ] = ( src [ 4 ] + surround_left_distributed ) / 1.5f ; /* BL */
dst [ 5 ] = ( src [ 5 ] + surround_right_distributed ) / 1.5f ; /* BR */
}
cvt - > len_cvt / = 8 ;
cvt - > len_cvt * = 6 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
/* Convert from 5.1 to quad. Distribute center across front, discard LFE. */
2015-06-21 17:33:46 +02:00
static void SDLCALL
2017-01-08 22:18:49 +01:00
SDL_Convert51ToQuad ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
2015-06-21 17:33:46 +02:00
{
2016-11-05 07:34:38 +01:00
float * dst = ( float * ) cvt - > buf ;
const float * src = dst ;
2015-06-21 17:33:46 +02:00
int i ;
2017-01-08 22:18:49 +01:00
LOG_DEBUG_CONVERT ( " 5.1 " , " quad " ) ;
2016-11-05 07:34:38 +01:00
SDL_assert ( format = = AUDIO_F32SYS ) ;
2015-06-21 17:33:46 +02:00
2017-08-29 06:41:45 +02:00
/* SDL's 4.0 layout: FL+FR+BL+BR */
/* SDL's 5.1 layout: FL+FR+FC+LFE+BL+BR */
2016-11-05 07:34:38 +01:00
for ( i = cvt - > len_cvt / ( sizeof ( float ) * 6 ) ; i ; - - i , src + = 6 , dst + = 4 ) {
2017-08-29 06:41:45 +02:00
const float front_center_distributed = src [ 2 ] * 0.5f ;
dst [ 0 ] = ( src [ 0 ] + front_center_distributed ) / 1.5f ; /* FL */
dst [ 1 ] = ( src [ 1 ] + front_center_distributed ) / 1.5f ; /* FR */
dst [ 2 ] = src [ 4 ] / 1.5f ; /* BL */
dst [ 3 ] = src [ 5 ] / 1.5f ; /* BR */
2015-06-21 17:33:46 +02:00
}
cvt - > len_cvt / = 6 ;
cvt - > len_cvt * = 4 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
2017-01-08 22:18:49 +01:00
2017-08-29 06:41:45 +02:00
/* Upmix mono to stereo (by duplication) */
2015-06-21 17:33:46 +02:00
static void SDLCALL
2017-01-08 22:18:49 +01:00
SDL_ConvertMonoToStereo ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
2015-06-21 17:33:46 +02:00
{
2016-11-05 07:34:38 +01:00
const float * src = ( const float * ) ( cvt - > buf + cvt - > len_cvt ) ;
float * dst = ( float * ) ( cvt - > buf + cvt - > len_cvt * 2 ) ;
2015-06-21 17:33:46 +02:00
int i ;
2016-11-05 07:34:38 +01:00
LOG_DEBUG_CONVERT ( " mono " , " stereo " ) ;
SDL_assert ( format = = AUDIO_F32SYS ) ;
2015-06-21 17:33:46 +02:00
2016-11-05 07:34:38 +01:00
for ( i = cvt - > len_cvt / sizeof ( float ) ; i ; - - i ) {
src - - ;
dst - = 2 ;
dst [ 0 ] = dst [ 1 ] = * src ;
2015-06-21 17:33:46 +02:00
}
cvt - > len_cvt * = 2 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
2017-08-29 06:41:45 +02:00
/* Upmix stereo to a pseudo-5.1 stream */
2015-06-21 17:33:46 +02:00
static void SDLCALL
2017-01-08 22:18:49 +01:00
SDL_ConvertStereoTo51 ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
2015-06-21 17:33:46 +02:00
{
int i ;
2016-11-05 07:34:38 +01:00
float lf , rf , ce ;
const float * src = ( const float * ) ( cvt - > buf + cvt - > len_cvt ) ;
float * dst = ( float * ) ( cvt - > buf + cvt - > len_cvt * 3 ) ;
LOG_DEBUG_CONVERT ( " stereo " , " 5.1 " ) ;
SDL_assert ( format = = AUDIO_F32SYS ) ;
2017-08-29 06:41:45 +02:00
for ( i = cvt - > len_cvt / ( sizeof ( float ) * 2 ) ; i ; - - i ) {
2016-11-05 07:34:38 +01:00
dst - = 6 ;
src - = 2 ;
lf = src [ 0 ] ;
rf = src [ 1 ] ;
2017-01-08 22:18:49 +01:00
ce = ( lf + rf ) * 0.5f ;
2017-08-29 06:41:45 +02:00
/* !!! FIXME: FL and FR may clip */
2017-01-08 22:18:49 +01:00
dst [ 0 ] = lf + ( lf - ce ) ; /* FL */
dst [ 1 ] = rf + ( rf - ce ) ; /* FR */
dst [ 2 ] = ce ; /* FC */
2017-08-29 06:41:45 +02:00
dst [ 3 ] = 0 ; /* LFE (only meant for special LFE effects) */
2017-01-08 22:18:49 +01:00
dst [ 4 ] = lf ; /* BL */
dst [ 5 ] = rf ; /* BR */
2015-06-21 17:33:46 +02:00
}
2016-11-05 07:34:38 +01:00
2015-06-21 17:33:46 +02:00
cvt - > len_cvt * = 3 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
2017-08-29 06:41:45 +02:00
/* Upmix quad to a pseudo-5.1 stream */
static void SDLCALL
SDL_ConvertQuadTo51 ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
{
int i ;
float lf , rf , lb , rb , ce ;
const float * src = ( const float * ) ( cvt - > buf + cvt - > len_cvt ) ;
float * dst = ( float * ) ( cvt - > buf + cvt - > len_cvt * 3 / 2 ) ;
LOG_DEBUG_CONVERT ( " quad " , " 5.1 " ) ;
SDL_assert ( format = = AUDIO_F32SYS ) ;
SDL_assert ( cvt - > len_cvt % ( sizeof ( float ) * 4 ) = = 0 ) ;
for ( i = cvt - > len_cvt / ( sizeof ( float ) * 4 ) ; i ; - - i ) {
dst - = 6 ;
src - = 4 ;
lf = src [ 0 ] ;
rf = src [ 1 ] ;
lb = src [ 2 ] ;
rb = src [ 3 ] ;
ce = ( lf + rf ) * 0.5f ;
/* !!! FIXME: FL and FR may clip */
dst [ 0 ] = lf + ( lf - ce ) ; /* FL */
dst [ 1 ] = rf + ( rf - ce ) ; /* FR */
dst [ 2 ] = ce ; /* FC */
dst [ 3 ] = 0 ; /* LFE (only meant for special LFE effects) */
dst [ 4 ] = lb ; /* BL */
dst [ 5 ] = rb ; /* BR */
}
cvt - > len_cvt = cvt - > len_cvt * 3 / 2 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
/* Upmix stereo to a pseudo-4.0 stream (by duplication) */
2015-06-21 17:33:46 +02:00
static void SDLCALL
2017-01-08 22:18:49 +01:00
SDL_ConvertStereoToQuad ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
2015-06-21 17:33:46 +02:00
{
2016-11-05 07:34:38 +01:00
const float * src = ( const float * ) ( cvt - > buf + cvt - > len_cvt ) ;
float * dst = ( float * ) ( cvt - > buf + cvt - > len_cvt * 2 ) ;
2017-01-08 22:18:49 +01:00
float lf , rf ;
2015-06-21 17:33:46 +02:00
int i ;
2016-11-05 07:34:38 +01:00
LOG_DEBUG_CONVERT ( " stereo " , " quad " ) ;
SDL_assert ( format = = AUDIO_F32SYS ) ;
2017-08-29 06:41:45 +02:00
for ( i = cvt - > len_cvt / ( sizeof ( float ) * 2 ) ; i ; - - i ) {
2016-11-05 07:34:38 +01:00
dst - = 4 ;
src - = 2 ;
lf = src [ 0 ] ;
rf = src [ 1 ] ;
2017-01-08 22:18:49 +01:00
dst [ 0 ] = lf ; /* FL */
dst [ 1 ] = rf ; /* FR */
dst [ 2 ] = lf ; /* BL */
dst [ 3 ] = rf ; /* BR */
2015-06-21 17:33:46 +02:00
}
2016-11-05 07:34:38 +01:00
2015-06-21 17:33:46 +02:00
cvt - > len_cvt * = 2 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
2017-08-29 06:41:45 +02:00
/* Upmix 5.1 to 7.1 */
static void SDLCALL
SDL_Convert51To71 ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
{
float lf , rf , lb , rb , ls , rs ;
int i ;
const float * src = ( const float * ) ( cvt - > buf + cvt - > len_cvt ) ;
float * dst = ( float * ) ( cvt - > buf + cvt - > len_cvt * 4 / 3 ) ;
LOG_DEBUG_CONVERT ( " 5.1 " , " 7.1 " ) ;
SDL_assert ( format = = AUDIO_F32SYS ) ;
SDL_assert ( cvt - > len_cvt % ( sizeof ( float ) * 6 ) = = 0 ) ;
for ( i = cvt - > len_cvt / ( sizeof ( float ) * 6 ) ; i ; - - i ) {
dst - = 8 ;
src - = 6 ;
lf = src [ 0 ] ;
rf = src [ 1 ] ;
lb = src [ 4 ] ;
rb = src [ 5 ] ;
ls = ( lf + lb ) * 0.5f ;
rs = ( rf + rb ) * 0.5f ;
/* !!! FIXME: these four may clip */
lf + = lf - ls ;
rf + = rf - ls ;
lb + = lb - ls ;
rb + = rb - ls ;
dst [ 3 ] = src [ 3 ] ; /* LFE */
dst [ 2 ] = src [ 2 ] ; /* FC */
dst [ 7 ] = rs ; /* SR */
dst [ 6 ] = ls ; /* SL */
dst [ 5 ] = rb ; /* BR */
dst [ 4 ] = lb ; /* BL */
dst [ 1 ] = rf ; /* FR */
dst [ 0 ] = lf ; /* FL */
}
cvt - > len_cvt = cvt - > len_cvt * 4 / 3 ;
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
2017-09-21 08:51:14 +02:00
/* SDL's resampler uses a "bandlimited interpolation" algorithm:
https : //ccrma.stanford.edu/~jos/resample/ */
2017-08-29 06:41:45 +02:00
2017-09-21 08:51:14 +02:00
# define RESAMPLER_ZERO_CROSSINGS 5
# define RESAMPLER_BITS_PER_SAMPLE 16
# define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1))
# define RESAMPLER_FILTER_SIZE ((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) + 1)
2017-01-09 12:00:58 +01:00
2017-09-21 08:51:14 +02:00
/* This is a "modified" bessel function, so you can't use POSIX j0() */
static double
bessel ( const double x )
{
const double xdiv2 = x / 2.0 ;
double i0 = 1.0f ;
double f = 1.0f ;
int i = 1 ;
while ( SDL_TRUE ) {
const double diff = SDL_pow ( xdiv2 , i * 2 ) / SDL_pow ( f , 2 ) ;
if ( diff < 1.0e-21 f ) {
break ;
2017-01-23 05:48:15 +01:00
}
2017-09-21 08:51:14 +02:00
i0 + = diff ;
i + + ;
f * = ( double ) i ;
}
2017-01-23 05:48:15 +01:00
2017-09-21 08:51:14 +02:00
return i0 ;
}
/* build kaiser table with cardinal sine applied to it, and array of differences between elements. */
static void
kaiser_and_sinc ( float * table , float * diffs , const int tablelen , const double beta )
{
const int lenm1 = tablelen - 1 ;
const int lenm1div2 = lenm1 / 2 ;
int i ;
table [ 0 ] = 1.0f ;
for ( i = 1 ; i < tablelen ; i + + ) {
const double kaiser = bessel ( beta * SDL_sqrt ( 1.0 - SDL_pow ( ( ( i - lenm1 ) / 2.0 ) / lenm1div2 , 2.0 ) ) ) / bessel ( beta ) ;
table [ tablelen - i ] = ( float ) kaiser ;
2017-01-09 12:00:58 +01:00
}
2017-09-21 08:51:14 +02:00
for ( i = 1 ; i < tablelen ; i + + ) {
const float x = ( ( ( float ) i ) / ( ( float ) RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ) * ( ( float ) M_PI ) ;
table [ i ] * = SDL_sinf ( x ) / x ;
diffs [ i - 1 ] = table [ i ] - table [ i - 1 ] ;
}
diffs [ lenm1 ] = 0.0f ;
2017-01-09 12:00:58 +01:00
}
2017-09-21 08:51:14 +02:00
static SDL_SpinLock ResampleFilterSpinlock = 0 ;
static float * ResamplerFilter = NULL ;
static float * ResamplerFilterDifference = NULL ;
int
SDL_PrepareResampleFilter ( void )
2017-01-23 02:27:48 +01:00
{
2017-09-21 08:51:14 +02:00
SDL_AtomicLock ( & ResampleFilterSpinlock ) ;
if ( ! ResamplerFilter ) {
/* if dB > 50, beta=(0.1102 * (dB - 8.7)), according to Matlab. */
const double dB = 80.0 ;
const double beta = 0.1102 * ( dB - 8.7 ) ;
const size_t alloclen = RESAMPLER_FILTER_SIZE * sizeof ( float ) ;
ResamplerFilter = ( float * ) SDL_malloc ( alloclen ) ;
if ( ! ResamplerFilter ) {
SDL_AtomicUnlock ( & ResampleFilterSpinlock ) ;
return SDL_OutOfMemory ( ) ;
2017-01-23 02:27:48 +01:00
}
2017-09-21 08:51:14 +02:00
ResamplerFilterDifference = ( float * ) SDL_malloc ( alloclen ) ;
if ( ! ResamplerFilterDifference ) {
SDL_free ( ResamplerFilter ) ;
ResamplerFilter = NULL ;
SDL_AtomicUnlock ( & ResampleFilterSpinlock ) ;
return SDL_OutOfMemory ( ) ;
2017-01-23 02:27:48 +01:00
}
2017-09-21 08:51:14 +02:00
kaiser_and_sinc ( ResamplerFilter , ResamplerFilterDifference , RESAMPLER_FILTER_SIZE , beta ) ;
2017-01-23 02:27:48 +01:00
}
2017-09-21 08:51:14 +02:00
SDL_AtomicUnlock ( & ResampleFilterSpinlock ) ;
return 0 ;
2017-01-23 02:27:48 +01:00
}
2017-09-21 08:51:14 +02:00
void
SDL_FreeResampleFilter ( void )
2017-01-23 02:27:48 +01:00
{
2017-09-21 08:51:14 +02:00
SDL_free ( ResamplerFilter ) ;
SDL_free ( ResamplerFilterDifference ) ;
ResamplerFilter = NULL ;
ResamplerFilterDifference = NULL ;
}
2017-03-02 19:33:04 +01:00
2017-01-23 02:27:48 +01:00
2017-09-21 08:51:14 +02:00
static int
SDL_ResampleAudio ( const int chans , const int inrate , const int outrate ,
float * last_sample , const float * inbuf ,
const int inbuflen , float * outbuf , const int outbuflen )
{
const float outtimeincr = 1.0f / ( ( float ) outrate ) ;
const float ratio = ( ( float ) outrate ) / ( ( float ) inrate ) ;
/*const int padding_len = (ratio < 1.0f) ? (int) SDL_ceilf(((float) (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * inrate) / ((float) outrate))) : RESAMPLER_SAMPLES_PER_ZERO_CROSSING;*/
const int framelen = chans * ( int ) sizeof ( float ) ;
const int inframes = inbuflen / framelen ;
const int wantedoutframes = ( int ) ( ( inbuflen / framelen ) * ratio ) ; /* outbuflen isn't total to write, it's total available. */
const int maxoutframes = outbuflen / framelen ;
const int outframes = ( wantedoutframes < maxoutframes ) ? wantedoutframes : maxoutframes ;
float * dst = outbuf ;
float outtime = 0.0f ;
int i , j , chan ;
for ( i = 0 ; i < outframes ; i + + ) {
const int srcindex = ( int ) ( outtime * inrate ) ;
const float finrate = ( float ) inrate ;
const float intime = ( ( float ) srcindex ) / finrate ;
const float innexttime = ( ( float ) ( srcindex + 1 ) ) / finrate ;
const float interpolation1 = 1.0f - ( innexttime - outtime ) / ( innexttime - intime ) ;
const int filterindex1 = ( int ) ( interpolation1 * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ;
const float interpolation2 = 1.0f - interpolation1 ;
const int filterindex2 = interpolation2 * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ;
for ( chan = 0 ; chan < chans ; chan + + ) {
float outsample = 0.0f ;
/* do this twice to calculate the sample, once for the "left wing" and then same for the right. */
/* !!! FIXME: do both wings in one loop */
for ( j = 0 ; ( filterindex1 + ( j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ) < RESAMPLER_FILTER_SIZE ; j + + ) {
/* !!! FIXME: insample uses zero for padding samples, but it should use prior state from last_sample. */
const int srcframe = srcindex - j ;
const float insample = ( srcframe < 0 ) ? 0.0f : inbuf [ ( srcframe * chans ) + chan ] ; /* !!! FIXME: we can bubble this conditional out of here by doing a pre loop. */
outsample + = ( insample * ( ResamplerFilter [ filterindex1 + ( j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ] + ( interpolation1 * ResamplerFilterDifference [ filterindex1 + ( j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ] ) ) ) ;
}
2017-01-23 02:27:48 +01:00
2017-09-21 08:51:14 +02:00
for ( j = 0 ; ( filterindex2 + ( j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ) < RESAMPLER_FILTER_SIZE ; j + + ) {
const int srcframe = srcindex + 1 + j ;
/* !!! FIXME: insample uses zero for padding samples, but it should use prior state from last_sample. */
const float insample = ( srcframe > = inframes ) ? 0.0f : inbuf [ ( srcframe * chans ) + chan ] ; /* !!! FIXME: we can bubble this conditional out of here by doing a post loop. */
outsample + = ( insample * ( ResamplerFilter [ filterindex2 + ( j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ] + ( interpolation2 * ResamplerFilterDifference [ filterindex2 + ( j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ] ) ) ) ;
}
* ( dst + + ) = outsample ;
}
outtime + = outtimeincr ;
2017-01-23 02:27:48 +01:00
}
2017-09-21 08:51:14 +02:00
return outframes * chans * sizeof ( float ) ;
}
2015-06-21 17:33:46 +02:00
int
SDL_ConvertAudio ( SDL_AudioCVT * cvt )
{
/* !!! FIXME: (cvt) should be const; stack-copy it here. */
/* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
/* Make sure there's data to convert */
if ( cvt - > buf = = NULL ) {
2016-11-05 07:34:38 +01:00
return SDL_SetError ( " No buffer allocated for conversion " ) ;
2015-06-21 17:33:46 +02:00
}
2016-11-05 07:34:38 +01:00
2015-06-21 17:33:46 +02:00
/* Return okay if no conversion is necessary */
cvt - > len_cvt = cvt - > len ;
if ( cvt - > filters [ 0 ] = = NULL ) {
2016-11-05 07:34:38 +01:00
return 0 ;
2015-06-21 17:33:46 +02:00
}
/* Set up the conversion and go! */
cvt - > filter_index = 0 ;
cvt - > filters [ 0 ] ( cvt , cvt - > src_format ) ;
2016-11-05 07:34:38 +01:00
return 0 ;
2015-06-21 17:33:46 +02:00
}
2016-11-05 07:34:38 +01:00
static void SDLCALL
SDL_Convert_Byteswap ( SDL_AudioCVT * cvt , SDL_AudioFormat format )
2015-06-21 17:33:46 +02:00
{
2016-11-05 09:52:28 +01:00
# if DEBUG_CONVERT
printf ( " Converting byte order \n " ) ;
# endif
2016-11-05 07:34:38 +01:00
switch ( SDL_AUDIO_BITSIZE ( format ) ) {
# define CASESWAP(b) \
case b : { \
Uint # # b * ptr = ( Uint # # b * ) cvt - > buf ; \
int i ; \
for ( i = cvt - > len_cvt / sizeof ( * ptr ) ; i ; - - i , + + ptr ) { \
* ptr = SDL_Swap # # b ( * ptr ) ; \
} \
break ; \
}
CASESWAP ( 16 ) ;
CASESWAP ( 32 ) ;
CASESWAP ( 64 ) ;
# undef CASESWAP
default : SDL_assert ( ! " unhandled byteswap datatype! " ) ; break ;
}
2015-06-21 17:33:46 +02:00
2016-11-05 07:34:38 +01:00
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
/* flip endian flag for data. */
if ( format & SDL_AUDIO_MASK_ENDIAN ) {
format & = ~ SDL_AUDIO_MASK_ENDIAN ;
} else {
format | = SDL_AUDIO_MASK_ENDIAN ;
}
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
2015-06-21 17:33:46 +02:00
}
2017-06-13 01:39:15 +02:00
static int
SDL_AddAudioCVTFilter ( SDL_AudioCVT * cvt , const SDL_AudioFilter filter )
{
if ( cvt - > filter_index > = SDL_AUDIOCVT_MAX_FILTERS ) {
return SDL_SetError ( " Too many filters needed for conversion, exceeded maximum of %d " , SDL_AUDIOCVT_MAX_FILTERS ) ;
}
if ( filter = = NULL ) {
return SDL_SetError ( " Audio filter pointer is NULL " ) ;
}
cvt - > filters [ cvt - > filter_index + + ] = filter ;
cvt - > filters [ cvt - > filter_index ] = NULL ; /* Moving terminator */
return 0 ;
}
2015-06-21 17:33:46 +02:00
static int
2016-11-05 07:34:38 +01:00
SDL_BuildAudioTypeCVTToFloat ( SDL_AudioCVT * cvt , const SDL_AudioFormat src_fmt )
2015-06-21 17:33:46 +02:00
{
2016-11-05 07:34:38 +01:00
int retval = 0 ; /* 0 == no conversion necessary. */
2015-06-21 17:33:46 +02:00
2016-11-05 07:34:38 +01:00
if ( ( SDL_AUDIO_ISBIGENDIAN ( src_fmt ) ! = 0 ) = = ( SDL_BYTEORDER = = SDL_LIL_ENDIAN ) ) {
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , SDL_Convert_Byteswap ) < 0 ) {
return - 1 ;
}
2016-11-05 07:34:38 +01:00
retval = 1 ; /* added a converter. */
}
2015-06-21 17:33:46 +02:00
2016-11-05 07:34:38 +01:00
if ( ! SDL_AUDIO_ISFLOAT ( src_fmt ) ) {
2016-11-05 08:53:59 +01:00
const Uint16 src_bitsize = SDL_AUDIO_BITSIZE ( src_fmt ) ;
const Uint16 dst_bitsize = 32 ;
2016-11-05 07:34:38 +01:00
SDL_AudioFilter filter = NULL ;
2016-11-05 08:53:59 +01:00
2016-11-05 07:34:38 +01:00
switch ( src_fmt & ~ SDL_AUDIO_MASK_ENDIAN ) {
case AUDIO_S8 : filter = SDL_Convert_S8_to_F32 ; break ;
case AUDIO_U8 : filter = SDL_Convert_U8_to_F32 ; break ;
case AUDIO_S16 : filter = SDL_Convert_S16_to_F32 ; break ;
2016-11-07 21:10:01 +01:00
case AUDIO_U16 : filter = SDL_Convert_U16_to_F32 ; break ;
2016-11-05 07:34:38 +01:00
case AUDIO_S32 : filter = SDL_Convert_S32_to_F32 ; break ;
default : SDL_assert ( ! " Unexpected audio format! " ) ; break ;
2015-06-21 17:33:46 +02:00
}
2016-11-05 07:34:38 +01:00
if ( ! filter ) {
2017-08-18 22:52:19 +02:00
return SDL_SetError ( " No conversion from source format to float available " ) ;
2016-11-05 07:34:38 +01:00
}
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , filter ) < 0 ) {
return - 1 ;
}
2015-06-21 17:33:46 +02:00
if ( src_bitsize < dst_bitsize ) {
const int mult = ( dst_bitsize / src_bitsize ) ;
cvt - > len_mult * = mult ;
cvt - > len_ratio * = mult ;
} else if ( src_bitsize > dst_bitsize ) {
cvt - > len_ratio / = ( src_bitsize / dst_bitsize ) ;
}
2016-11-05 08:53:59 +01:00
2016-11-05 07:34:38 +01:00
retval = 1 ; /* added a converter. */
2015-06-21 17:33:46 +02:00
}
2016-11-05 07:34:38 +01:00
return retval ;
2015-06-21 17:33:46 +02:00
}
2016-11-05 07:34:38 +01:00
static int
SDL_BuildAudioTypeCVTFromFloat ( SDL_AudioCVT * cvt , const SDL_AudioFormat dst_fmt )
2015-06-21 17:33:46 +02:00
{
2016-11-05 07:34:38 +01:00
int retval = 0 ; /* 0 == no conversion necessary. */
if ( ! SDL_AUDIO_ISFLOAT ( dst_fmt ) ) {
2016-11-05 08:56:55 +01:00
const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE ( dst_fmt ) ;
const Uint16 src_bitsize = 32 ;
2016-11-05 07:34:38 +01:00
SDL_AudioFilter filter = NULL ;
switch ( dst_fmt & ~ SDL_AUDIO_MASK_ENDIAN ) {
case AUDIO_S8 : filter = SDL_Convert_F32_to_S8 ; break ;
case AUDIO_U8 : filter = SDL_Convert_F32_to_U8 ; break ;
case AUDIO_S16 : filter = SDL_Convert_F32_to_S16 ; break ;
2016-11-07 21:10:01 +01:00
case AUDIO_U16 : filter = SDL_Convert_F32_to_U16 ; break ;
2016-11-05 07:34:38 +01:00
case AUDIO_S32 : filter = SDL_Convert_F32_to_S32 ; break ;
default : SDL_assert ( ! " Unexpected audio format! " ) ; break ;
}
if ( ! filter ) {
2017-08-18 22:52:19 +02:00
return SDL_SetError ( " No conversion from float to destination format available " ) ;
2016-11-05 07:34:38 +01:00
}
2015-06-21 17:33:46 +02:00
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , filter ) < 0 ) {
return - 1 ;
}
2016-11-05 07:34:38 +01:00
if ( src_bitsize < dst_bitsize ) {
const int mult = ( dst_bitsize / src_bitsize ) ;
cvt - > len_mult * = mult ;
cvt - > len_ratio * = mult ;
} else if ( src_bitsize > dst_bitsize ) {
cvt - > len_ratio / = ( src_bitsize / dst_bitsize ) ;
}
retval = 1 ; /* added a converter. */
}
if ( ( SDL_AUDIO_ISBIGENDIAN ( dst_fmt ) ! = 0 ) = = ( SDL_BYTEORDER = = SDL_LIL_ENDIAN ) ) {
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , SDL_Convert_Byteswap ) < 0 ) {
return - 1 ;
}
2016-11-05 07:34:38 +01:00
retval = 1 ; /* added a converter. */
}
return retval ;
2015-06-21 17:33:46 +02:00
}
2017-01-09 12:00:58 +01:00
static void
SDL_ResampleCVT ( SDL_AudioCVT * cvt , const int chans , const SDL_AudioFormat format )
{
2017-09-21 08:51:14 +02:00
/* !!! FIXME in 2.1: there are ten slots in the filter list, and the theoretical maximum we use is six (seven with NULL terminator).
! ! ! FIXME in 2.1 : We need to store data for this resampler , because the cvt structure doesn ' t store the original sample rates ,
! ! ! FIXME in 2.1 : so we steal the ninth and tenth slot . : ( */
const int srcrate = ( int ) ( size_t ) cvt - > filters [ SDL_AUDIOCVT_MAX_FILTERS - 1 ] ;
const int dstrate = ( int ) ( size_t ) cvt - > filters [ SDL_AUDIOCVT_MAX_FILTERS ] ;
2017-01-09 12:00:58 +01:00
const float * src = ( const float * ) cvt - > buf ;
const int srclen = cvt - > len_cvt ;
2017-09-21 08:51:14 +02:00
/*float *dst = (float *) cvt->buf;
const int dstlen = ( cvt - > len * cvt - > len_mult ) ; */
/* !!! FIXME: remove this if we can get the resampler to work in-place again. */
float * dst = ( float * ) ( cvt - > buf + srclen ) ;
const int dstlen = ( cvt - > len * cvt - > len_mult ) - srclen ;
2017-01-09 22:31:57 +01:00
float state [ 8 ] ;
2017-01-09 12:00:58 +01:00
SDL_assert ( format = = AUDIO_F32SYS ) ;
2017-09-21 08:51:14 +02:00
SDL_zero ( state ) ;
cvt - > len_cvt = SDL_ResampleAudio ( chans , srcrate , dstrate , state , src , srclen , dst , dstlen ) ;
SDL_memcpy ( cvt - > buf , dst , cvt - > len_cvt ) ; /* !!! FIXME: remove this if we can get the resampler to work in-place again. */
2017-01-09 12:00:58 +01:00
if ( cvt - > filters [ + + cvt - > filter_index ] ) {
cvt - > filters [ cvt - > filter_index ] ( cvt , format ) ;
}
}
/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't
! ! ! FIXME : store channel info , so we have to have function entry
! ! ! FIXME : points for each supported channel count and multiple
! ! ! FIXME : vs arbitrary . When we rev the ABI , clean this up . */
2016-11-05 07:34:38 +01:00
# define RESAMPLER_FUNCS(chans) \
static void SDLCALL \
2017-01-09 12:00:58 +01:00
SDL_ResampleCVT_c # # chans ( SDL_AudioCVT * cvt , SDL_AudioFormat format ) { \
SDL_ResampleCVT ( cvt , chans , format ) ; \
2016-11-05 07:34:38 +01:00
}
RESAMPLER_FUNCS ( 1 )
RESAMPLER_FUNCS ( 2 )
RESAMPLER_FUNCS ( 4 )
RESAMPLER_FUNCS ( 6 )
RESAMPLER_FUNCS ( 8 )
# undef RESAMPLER_FUNCS
2017-01-06 01:12:20 +01:00
static SDL_AudioFilter
2017-01-09 12:00:58 +01:00
ChooseCVTResampler ( const int dst_channels )
2017-01-06 01:12:20 +01:00
{
2017-01-09 12:00:58 +01:00
switch ( dst_channels ) {
case 1 : return SDL_ResampleCVT_c1 ;
case 2 : return SDL_ResampleCVT_c2 ;
case 4 : return SDL_ResampleCVT_c4 ;
case 6 : return SDL_ResampleCVT_c6 ;
case 8 : return SDL_ResampleCVT_c8 ;
default : break ;
2017-01-06 01:12:20 +01:00
}
2017-01-09 12:00:58 +01:00
return NULL ;
2017-01-06 01:12:20 +01:00
}
static int
SDL_BuildAudioResampleCVT ( SDL_AudioCVT * cvt , const int dst_channels ,
const int src_rate , const int dst_rate )
{
SDL_AudioFilter filter ;
if ( src_rate = = dst_rate ) {
return 0 ; /* no conversion necessary. */
}
2017-01-09 12:00:58 +01:00
filter = ChooseCVTResampler ( dst_channels ) ;
2017-01-06 01:12:20 +01:00
if ( filter = = NULL ) {
return SDL_SetError ( " No conversion available for these rates " ) ;
}
2015-06-21 17:33:46 +02:00
2017-09-21 08:51:14 +02:00
if ( SDL_PrepareResampleFilter ( ) < 0 ) {
return - 1 ;
}
2017-01-06 01:12:20 +01:00
/* Update (cvt) with filter details... */
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , filter ) < 0 ) {
return - 1 ;
}
2017-09-21 08:51:14 +02:00
/* !!! FIXME in 2.1: there are ten slots in the filter list, and the theoretical maximum we use is six (seven with NULL terminator).
! ! ! FIXME in 2.1 : We need to store data for this resampler , because the cvt structure doesn ' t store the original sample rates ,
! ! ! FIXME in 2.1 : so we steal the ninth and tenth slot . : ( */
if ( cvt - > filter_index > = ( SDL_AUDIOCVT_MAX_FILTERS - 2 ) ) {
return SDL_SetError ( " Too many filters needed for conversion, exceeded maximum of %d " , SDL_AUDIOCVT_MAX_FILTERS - 2 ) ;
}
cvt - > filters [ SDL_AUDIOCVT_MAX_FILTERS - 1 ] = ( SDL_AudioFilter ) ( size_t ) src_rate ;
cvt - > filters [ SDL_AUDIOCVT_MAX_FILTERS ] = ( SDL_AudioFilter ) ( size_t ) dst_rate ;
2017-01-06 01:12:20 +01:00
if ( src_rate < dst_rate ) {
const double mult = ( ( double ) dst_rate ) / ( ( double ) src_rate ) ;
cvt - > len_mult * = ( int ) SDL_ceil ( mult ) ;
cvt - > len_ratio * = mult ;
} else {
cvt - > len_ratio / = ( ( double ) src_rate ) / ( ( double ) dst_rate ) ;
2015-06-21 17:33:46 +02:00
}
2017-09-21 08:51:14 +02:00
/* !!! FIXME: remove this if we can get the resampler to work in-place again. */
/* the buffer is big enough to hold the destination now, but
we need it large enough to hold a separate scratch buffer . */
cvt - > len_mult * = 2 ;
2017-01-06 01:12:20 +01:00
return 1 ; /* added a converter. */
2015-06-21 17:33:46 +02:00
}
2017-06-13 03:35:24 +02:00
static SDL_bool
SDL_SupportedAudioFormat ( const SDL_AudioFormat fmt )
{
switch ( fmt ) {
case AUDIO_U8 :
case AUDIO_S8 :
case AUDIO_U16LSB :
case AUDIO_S16LSB :
case AUDIO_U16MSB :
case AUDIO_S16MSB :
case AUDIO_S32LSB :
case AUDIO_S32MSB :
case AUDIO_F32LSB :
case AUDIO_F32MSB :
return SDL_TRUE ; /* supported. */
default :
break ;
}
return SDL_FALSE ; /* unsupported. */
}
static SDL_bool
SDL_SupportedChannelCount ( const int channels )
{
switch ( channels ) {
case 1 : /* mono */
case 2 : /* stereo */
case 4 : /* quad */
case 6 : /* 5.1 */
2017-08-29 06:41:45 +02:00
case 8 : /* 7.1 */
return SDL_TRUE ; /* supported. */
2017-06-13 03:35:24 +02:00
default :
break ;
}
return SDL_FALSE ; /* unsupported. */
}
2015-06-21 17:33:46 +02:00
/* Creates a set of audio filters to convert from one format to another.
2017-08-18 22:52:19 +02:00
Returns 0 if no conversion is needed , 1 if the audio filter is set up ,
or - 1 if an error like invalid parameter , unsupported format , etc . occurred .
2015-06-21 17:33:46 +02:00
*/
int
SDL_BuildAudioCVT ( SDL_AudioCVT * cvt ,
SDL_AudioFormat src_fmt , Uint8 src_channels , int src_rate ,
SDL_AudioFormat dst_fmt , Uint8 dst_channels , int dst_rate )
{
/* Sanity check target pointer */
if ( cvt = = NULL ) {
return SDL_InvalidParamError ( " cvt " ) ;
}
2017-01-06 08:53:46 +01:00
/* Make sure we zero out the audio conversion before error checking */
SDL_zerop ( cvt ) ;
2017-06-13 03:35:24 +02:00
if ( ! SDL_SupportedAudioFormat ( src_fmt ) ) {
2015-06-21 17:33:46 +02:00
return SDL_SetError ( " Invalid source format " ) ;
2017-06-13 03:35:24 +02:00
} else if ( ! SDL_SupportedAudioFormat ( dst_fmt ) ) {
2015-06-21 17:33:46 +02:00
return SDL_SetError ( " Invalid destination format " ) ;
2017-06-13 03:35:24 +02:00
} else if ( ! SDL_SupportedChannelCount ( src_channels ) ) {
return SDL_SetError ( " Invalid source channels " ) ;
} else if ( ! SDL_SupportedChannelCount ( dst_channels ) ) {
return SDL_SetError ( " Invalid destination channels " ) ;
} else if ( src_rate = = 0 ) {
return SDL_SetError ( " Source rate is zero " ) ;
} else if ( dst_rate = = 0 ) {
return SDL_SetError ( " Destination rate is zero " ) ;
2015-06-21 17:33:46 +02:00
}
2016-11-05 09:52:28 +01:00
# if DEBUG_CONVERT
2015-06-21 17:33:46 +02:00
printf ( " Build format %04x->%04x, channels %u->%u, rate %d->%d \n " ,
src_fmt , dst_fmt , src_channels , dst_channels , src_rate , dst_rate ) ;
# endif
/* Start off with no conversion necessary */
cvt - > src_format = src_fmt ;
cvt - > dst_format = dst_fmt ;
cvt - > needed = 0 ;
cvt - > filter_index = 0 ;
2017-09-21 08:51:14 +02:00
SDL_zero ( cvt - > filters ) ;
2015-06-21 17:33:46 +02:00
cvt - > len_mult = 1 ;
cvt - > len_ratio = 1.0 ;
cvt - > rate_incr = ( ( double ) dst_rate ) / ( ( double ) src_rate ) ;
2017-08-29 06:42:39 +02:00
/* Make sure we've chosen audio conversion functions (MMX, scalar, etc.) */
SDL_ChooseAudioConverters ( ) ;
2016-11-05 07:34:38 +01:00
/* Type conversion goes like this now:
- byteswap to CPU native format first if necessary .
- convert to native Float32 if necessary .
- resample and change channel count if necessary .
- convert back to native format .
- byteswap back to foreign format if necessary .
The expectation is we can process data faster in float32
( possibly with SIMD ) , and making several passes over the same
2017-01-06 01:12:20 +01:00
buffer is likely to be CPU cache - friendly , avoiding the
2016-11-05 07:34:38 +01:00
biggest performance hit in modern times . Previously we had
( script - generated ) custom converters for every data type and
it was a bloat on SDL compile times and final library size . */
2017-01-06 08:53:46 +01:00
/* see if we can skip float conversion entirely. */
if ( src_rate = = dst_rate & & src_channels = = dst_channels ) {
if ( src_fmt = = dst_fmt ) {
return 0 ;
}
/* just a byteswap needed? */
if ( ( src_fmt & ~ SDL_AUDIO_MASK_ENDIAN ) = = ( dst_fmt & ~ SDL_AUDIO_MASK_ENDIAN ) ) {
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , SDL_Convert_Byteswap ) < 0 ) {
return - 1 ;
}
2017-01-06 08:53:46 +01:00
cvt - > needed = 1 ;
return 1 ;
}
2016-11-05 07:34:38 +01:00
}
2015-06-21 17:33:46 +02:00
/* Convert data types, if necessary. Updates (cvt). */
2017-01-06 08:53:46 +01:00
if ( SDL_BuildAudioTypeCVTToFloat ( cvt , src_fmt ) < 0 ) {
2015-06-21 17:33:46 +02:00
return - 1 ; /* shouldn't happen, but just in case... */
}
/* Channel conversion */
2017-08-29 06:41:45 +02:00
if ( src_channels < dst_channels ) {
/* Upmixing */
/* Mono -> Stereo [-> ...] */
2015-06-21 17:33:46 +02:00
if ( ( src_channels = = 1 ) & & ( dst_channels > 1 ) ) {
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , SDL_ConvertMonoToStereo ) < 0 ) {
return - 1 ;
}
2015-06-21 17:33:46 +02:00
cvt - > len_mult * = 2 ;
src_channels = 2 ;
cvt - > len_ratio * = 2 ;
}
2017-08-29 06:41:45 +02:00
/* [Mono ->] Stereo -> 5.1 [-> 7.1] */
if ( ( src_channels = = 2 ) & & ( dst_channels > = 6 ) ) {
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , SDL_ConvertStereoTo51 ) < 0 ) {
return - 1 ;
}
2015-06-21 17:33:46 +02:00
src_channels = 6 ;
cvt - > len_mult * = 3 ;
cvt - > len_ratio * = 3 ;
}
2017-08-29 06:41:45 +02:00
/* Quad -> 5.1 [-> 7.1] */
if ( ( src_channels = = 4 ) & & ( dst_channels > = 6 ) ) {
if ( SDL_AddAudioCVTFilter ( cvt , SDL_ConvertQuadTo51 ) < 0 ) {
return - 1 ;
}
src_channels = 6 ;
cvt - > len_mult = ( cvt - > len_mult * 3 + 1 ) / 2 ;
cvt - > len_ratio * = 1.5 ;
}
/* [[Mono ->] Stereo ->] 5.1 -> 7.1 */
if ( ( src_channels = = 6 ) & & ( dst_channels = = 8 ) ) {
if ( SDL_AddAudioCVTFilter ( cvt , SDL_Convert51To71 ) < 0 ) {
return - 1 ;
}
src_channels = 8 ;
cvt - > len_mult = ( cvt - > len_mult * 4 + 2 ) / 3 ;
/* Should be numerically exact with every valid input to this
function */
cvt - > len_ratio = cvt - > len_ratio * 4 / 3 ;
}
/* [Mono ->] Stereo -> Quad */
2015-06-21 17:33:46 +02:00
if ( ( src_channels = = 2 ) & & ( dst_channels = = 4 ) ) {
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , SDL_ConvertStereoToQuad ) < 0 ) {
return - 1 ;
}
2015-06-21 17:33:46 +02:00
src_channels = 4 ;
cvt - > len_mult * = 2 ;
cvt - > len_ratio * = 2 ;
}
2017-08-29 06:41:45 +02:00
} else if ( src_channels > dst_channels ) {
/* Downmixing */
/* 7.1 -> 5.1 [-> Stereo [-> Mono]] */
/* 7.1 -> 5.1 [-> Quad] */
if ( ( src_channels = = 8 ) & & ( dst_channels < = 6 ) ) {
if ( SDL_AddAudioCVTFilter ( cvt , SDL_Convert71To51 ) < 0 ) {
2017-06-13 01:39:15 +02:00
return - 1 ;
}
2017-08-29 06:41:45 +02:00
src_channels = 6 ;
cvt - > len_ratio * = 0.75 ;
2015-06-21 17:33:46 +02:00
}
2017-08-29 06:41:45 +02:00
/* [7.1 ->] 5.1 -> Stereo [-> Mono] */
2015-06-21 17:33:46 +02:00
if ( ( src_channels = = 6 ) & & ( dst_channels < = 2 ) ) {
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , SDL_Convert51ToStereo ) < 0 ) {
return - 1 ;
}
2015-06-21 17:33:46 +02:00
src_channels = 2 ;
cvt - > len_ratio / = 3 ;
}
2017-08-29 06:41:45 +02:00
/* 5.1 -> Quad */
2015-06-21 17:33:46 +02:00
if ( ( src_channels = = 6 ) & & ( dst_channels = = 4 ) ) {
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , SDL_Convert51ToQuad ) < 0 ) {
return - 1 ;
}
2015-06-21 17:33:46 +02:00
src_channels = 4 ;
2017-08-29 06:41:45 +02:00
cvt - > len_ratio = cvt - > len_ratio * 2 / 3 ;
}
/* Quad -> Stereo [-> Mono] */
if ( ( src_channels = = 4 ) & & ( dst_channels < = 2 ) ) {
if ( SDL_AddAudioCVTFilter ( cvt , SDL_ConvertQuadToStereo ) < 0 ) {
return - 1 ;
}
src_channels = 2 ;
2015-06-21 17:33:46 +02:00
cvt - > len_ratio / = 2 ;
}
2017-08-29 06:41:45 +02:00
/* [... ->] Stereo -> Mono */
if ( ( src_channels = = 2 ) & & ( dst_channels = = 1 ) ) {
2017-01-23 06:57:19 +01:00
SDL_AudioFilter filter = NULL ;
# if HAVE_SSE3_INTRINSICS
if ( SDL_HasSSE3 ( ) ) {
filter = SDL_ConvertStereoToMono_SSE3 ;
}
# endif
if ( ! filter ) {
filter = SDL_ConvertStereoToMono ;
}
2017-06-13 01:39:15 +02:00
if ( SDL_AddAudioCVTFilter ( cvt , filter ) < 0 ) {
return - 1 ;
}
2017-01-23 06:57:19 +01:00
2017-08-29 06:41:45 +02:00
src_channels = 1 ;
2015-06-21 17:33:46 +02:00
cvt - > len_ratio / = 2 ;
}
}
2017-08-29 06:41:45 +02:00
if ( src_channels ! = dst_channels ) {
/* All combinations of supported channel counts should have been
handled by now , but let ' s be defensive */
return SDL_SetError ( " Invalid channel combination " ) ;
}
2015-06-21 17:33:46 +02:00
/* Do rate conversion, if necessary. Updates (cvt). */
2017-01-06 08:53:46 +01:00
if ( SDL_BuildAudioResampleCVT ( cvt , dst_channels , src_rate , dst_rate ) < 0 ) {
2015-06-21 17:33:46 +02:00
return - 1 ; /* shouldn't happen, but just in case... */
}
2017-01-06 01:12:20 +01:00
/* Move to final data type. */
2017-01-06 08:53:46 +01:00
if ( SDL_BuildAudioTypeCVTFromFloat ( cvt , dst_fmt ) < 0 ) {
2016-11-05 07:34:38 +01:00
return - 1 ; /* shouldn't happen, but just in case... */
2015-06-21 17:33:46 +02:00
}
2016-11-05 07:34:38 +01:00
cvt - > needed = ( cvt - > filter_index ! = 0 ) ;
2015-06-21 17:33:46 +02:00
return ( cvt - > needed ) ;
}
2017-01-24 06:08:24 +01:00
typedef int ( * SDL_ResampleAudioStreamFunc ) ( SDL_AudioStream * stream , const void * inbuf , const int inbuflen , void * outbuf , const int outbuflen ) ;
2017-01-06 11:16:26 +01:00
typedef void ( * SDL_ResetAudioStreamResamplerFunc ) ( SDL_AudioStream * stream ) ;
typedef void ( * SDL_CleanupAudioStreamResamplerFunc ) ( SDL_AudioStream * stream ) ;
2017-01-06 01:29:38 +01:00
struct SDL_AudioStream
{
SDL_AudioCVT cvt_before_resampling ;
SDL_AudioCVT cvt_after_resampling ;
SDL_DataQueue * queue ;
2017-01-24 06:51:33 +01:00
Uint8 * work_buffer_base ; /* maybe unaligned pointer from SDL_realloc(). */
2017-01-06 01:29:38 +01:00
int work_buffer_len ;
int src_sample_frame_size ;
SDL_AudioFormat src_format ;
Uint8 src_channels ;
int src_rate ;
int dst_sample_frame_size ;
SDL_AudioFormat dst_format ;
Uint8 dst_channels ;
int dst_rate ;
double rate_incr ;
Uint8 pre_resample_channels ;
int packetlen ;
2017-01-06 11:16:26 +01:00
void * resampler_state ;
SDL_ResampleAudioStreamFunc resampler_func ;
SDL_ResetAudioStreamResamplerFunc reset_resampler_func ;
SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func ;
2017-01-06 01:29:38 +01:00
} ;
2017-01-25 02:30:48 +01:00
static Uint8 *
EnsureStreamBufferSize ( SDL_AudioStream * stream , const int newlen )
{
Uint8 * ptr ;
size_t offset ;
if ( stream - > work_buffer_len > = newlen ) {
ptr = stream - > work_buffer_base ;
} else {
ptr = ( Uint8 * ) SDL_realloc ( stream - > work_buffer_base , newlen + 32 ) ;
if ( ! ptr ) {
SDL_OutOfMemory ( ) ;
return NULL ;
}
/* Make sure we're aligned to 16 bytes for SIMD code. */
stream - > work_buffer_base = ptr ;
stream - > work_buffer_len = newlen ;
}
offset = ( ( size_t ) ptr ) & 15 ;
return offset ? ptr + ( 16 - offset ) : ptr ;
}
2017-01-07 05:43:53 +01:00
# ifdef HAVE_LIBSAMPLERATE_H
2017-01-06 11:16:26 +01:00
static int
2017-01-24 06:08:24 +01:00
SDL_ResampleAudioStream_SRC ( SDL_AudioStream * stream , const void * _inbuf , const int inbuflen , void * _outbuf , const int outbuflen )
2017-01-06 11:16:26 +01:00
{
2017-01-24 06:08:24 +01:00
const float * inbuf = ( const float * ) _inbuf ;
float * outbuf = ( float * ) _outbuf ;
2017-01-09 12:00:58 +01:00
const int framelen = sizeof ( float ) * stream - > pre_resample_channels ;
2017-01-08 20:18:03 +01:00
SRC_STATE * state = ( SRC_STATE * ) stream - > resampler_state ;
2017-01-06 11:16:26 +01:00
SRC_DATA data ;
int result ;
2017-01-25 02:30:48 +01:00
if ( inbuf = = ( ( const float * ) outbuf ) ) { /* libsamplerate can't work in-place. */
Uint8 * ptr = EnsureStreamBufferSize ( stream , inbuflen + outbuflen ) ;
if ( ptr = = NULL ) {
SDL_OutOfMemory ( ) ;
return 0 ;
}
SDL_memcpy ( ptr + outbuflen , ptr , inbuflen ) ;
inbuf = ( const float * ) ( ptr + outbuflen ) ;
outbuf = ( float * ) ptr ;
}
2017-01-07 05:43:53 +01:00
data . data_in = ( float * ) inbuf ; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
2017-01-09 12:00:58 +01:00
data . input_frames = inbuflen / framelen ;
2017-01-06 11:16:26 +01:00
data . input_frames_used = 0 ;
data . data_out = outbuf ;
2017-01-09 12:00:58 +01:00
data . output_frames = outbuflen / framelen ;
2017-01-06 11:16:26 +01:00
data . end_of_input = 0 ;
data . src_ratio = stream - > rate_incr ;
2017-01-08 20:18:03 +01:00
result = SRC_src_process ( state , & data ) ;
2017-01-06 11:16:26 +01:00
if ( result ! = 0 ) {
2017-01-08 20:18:03 +01:00
SDL_SetError ( " src_process() failed: %s " , SRC_src_strerror ( result ) ) ;
2017-01-06 11:16:26 +01:00
return 0 ;
}
/* If this fails, we need to store them off somewhere */
SDL_assert ( data . input_frames_used = = data . input_frames ) ;
return data . output_frames_gen * ( sizeof ( float ) * stream - > pre_resample_channels ) ;
}
static void
SDL_ResetAudioStreamResampler_SRC ( SDL_AudioStream * stream )
{
2017-01-08 20:18:03 +01:00
SRC_src_reset ( ( SRC_STATE * ) stream - > resampler_state ) ;
2017-01-06 11:16:26 +01:00
}
static void
SDL_CleanupAudioStreamResampler_SRC ( SDL_AudioStream * stream )
{
2017-01-08 20:18:03 +01:00
SRC_STATE * state = ( SRC_STATE * ) stream - > resampler_state ;
2017-01-06 11:16:26 +01:00
if ( state ) {
2017-01-08 20:18:03 +01:00
SRC_src_delete ( state ) ;
2017-01-06 11:16:26 +01:00
}
stream - > resampler_state = NULL ;
stream - > resampler_func = NULL ;
stream - > reset_resampler_func = NULL ;
stream - > cleanup_resampler_func = NULL ;
}
static SDL_bool
SetupLibSampleRateResampling ( SDL_AudioStream * stream )
{
2017-01-08 20:18:03 +01:00
int result = 0 ;
SRC_STATE * state = NULL ;
2017-01-06 11:16:26 +01:00
2017-01-08 20:18:03 +01:00
if ( SRC_available ) {
2017-01-24 21:52:22 +01:00
state = SRC_src_new ( SRC_converter , stream - > pre_resample_channels , & result ) ;
2017-01-08 20:18:03 +01:00
if ( ! state ) {
SDL_SetError ( " src_new() failed: %s " , SRC_src_strerror ( result ) ) ;
}
2017-01-06 11:16:26 +01:00
}
2017-01-08 20:18:03 +01:00
if ( ! state ) {
SDL_CleanupAudioStreamResampler_SRC ( stream ) ;
2017-01-06 11:16:26 +01:00
return SDL_FALSE ;
}
stream - > resampler_state = state ;
stream - > resampler_func = SDL_ResampleAudioStream_SRC ;
stream - > reset_resampler_func = SDL_ResetAudioStreamResampler_SRC ;
stream - > cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC ;
return SDL_TRUE ;
}
2017-01-07 05:43:53 +01:00
# endif /* HAVE_LIBSAMPLERATE_H */
2017-01-06 11:16:26 +01:00
2017-01-08 20:18:03 +01:00
2017-01-06 11:16:26 +01:00
typedef struct
{
SDL_bool resampler_seeded ;
2017-01-24 06:08:24 +01:00
union
{
float f [ 8 ] ;
Sint16 si16 [ 2 ] ;
} resampler_state ;
2017-01-06 11:16:26 +01:00
} SDL_AudioStreamResamplerState ;
static int
2017-01-24 06:08:24 +01:00
SDL_ResampleAudioStream ( SDL_AudioStream * stream , const void * _inbuf , const int inbuflen , void * _outbuf , const int outbuflen )
2017-01-06 11:16:26 +01:00
{
2017-01-24 06:08:24 +01:00
const float * inbuf = ( const float * ) _inbuf ;
float * outbuf = ( float * ) _outbuf ;
2017-01-06 11:16:26 +01:00
SDL_AudioStreamResamplerState * state = ( SDL_AudioStreamResamplerState * ) stream - > resampler_state ;
const int chans = ( int ) stream - > pre_resample_channels ;
2017-01-24 06:08:24 +01:00
SDL_assert ( chans < = SDL_arraysize ( state - > resampler_state . f ) ) ;
2017-01-06 11:16:26 +01:00
2017-09-21 08:51:14 +02:00
if ( inbuf = = ( ( const float * ) outbuf ) ) { /* !!! FIXME can't work in-place (for now!). */
Uint8 * ptr = EnsureStreamBufferSize ( stream , inbuflen + outbuflen ) ;
if ( ptr = = NULL ) {
SDL_OutOfMemory ( ) ;
return 0 ;
}
SDL_memcpy ( ptr + outbuflen , ptr , inbuflen ) ;
inbuf = ( const float * ) ( ptr + outbuflen ) ;
outbuf = ( float * ) ptr ;
2017-01-06 11:16:26 +01:00
}
2017-01-24 06:08:24 +01:00
if ( ! state - > resampler_seeded ) {
2017-09-21 08:51:14 +02:00
SDL_zero ( state - > resampler_state . f ) ;
2017-01-24 06:08:24 +01:00
state - > resampler_seeded = SDL_TRUE ;
}
2017-09-21 08:51:14 +02:00
return SDL_ResampleAudio ( chans , stream - > src_rate , stream - > dst_rate , state - > resampler_state . f , inbuf , inbuflen , outbuf , outbuflen ) ;
2017-01-06 11:16:26 +01:00
}
static void
SDL_ResetAudioStreamResampler ( SDL_AudioStream * stream )
{
SDL_AudioStreamResamplerState * state = ( SDL_AudioStreamResamplerState * ) stream - > resampler_state ;
state - > resampler_seeded = SDL_FALSE ;
}
static void
SDL_CleanupAudioStreamResampler ( SDL_AudioStream * stream )
{
SDL_free ( stream - > resampler_state ) ;
}
2017-01-08 20:17:09 +01:00
SDL_AudioStream *
SDL_NewAudioStream ( const SDL_AudioFormat src_format ,
const Uint8 src_channels ,
const int src_rate ,
const SDL_AudioFormat dst_format ,
const Uint8 dst_channels ,
const int dst_rate )
2017-01-06 01:29:38 +01:00
{
const int packetlen = 4096 ; /* !!! FIXME: good enough for now. */
Uint8 pre_resample_channels ;
SDL_AudioStream * retval ;
retval = ( SDL_AudioStream * ) SDL_calloc ( 1 , sizeof ( SDL_AudioStream ) ) ;
if ( ! retval ) {
return NULL ;
}
/* If increasing channels, do it after resampling, since we'd just
do more work to resample duplicate channels . If we ' re decreasing , do
it first so we resample the interpolated data instead of interpolating
the resampled data ( ! ! ! FIXME : decide if that works in practice , though ! ) . */
pre_resample_channels = SDL_min ( src_channels , dst_channels ) ;
2017-02-13 22:56:41 +01:00
retval - > src_sample_frame_size = ( SDL_AUDIO_BITSIZE ( src_format ) / 8 ) * src_channels ;
2017-01-06 01:29:38 +01:00
retval - > src_format = src_format ;
retval - > src_channels = src_channels ;
retval - > src_rate = src_rate ;
2017-02-13 22:56:41 +01:00
retval - > dst_sample_frame_size = ( SDL_AUDIO_BITSIZE ( dst_format ) / 8 ) * dst_channels ;
2017-01-06 01:29:38 +01:00
retval - > dst_format = dst_format ;
retval - > dst_channels = dst_channels ;
retval - > dst_rate = dst_rate ;
retval - > pre_resample_channels = pre_resample_channels ;
retval - > packetlen = packetlen ;
retval - > rate_incr = ( ( double ) dst_rate ) / ( ( double ) src_rate ) ;
/* Not resampling? It's an easy conversion (and maybe not even that!). */
if ( src_rate = = dst_rate ) {
retval - > cvt_before_resampling . needed = SDL_FALSE ;
2017-01-06 11:16:26 +01:00
if ( SDL_BuildAudioCVT ( & retval - > cvt_after_resampling , src_format , src_channels , dst_rate , dst_format , dst_channels , dst_rate ) < 0 ) {
SDL_FreeAudioStream ( retval ) ;
2017-01-06 01:29:38 +01:00
return NULL ; /* SDL_BuildAudioCVT should have called SDL_SetError. */
}
} else {
/* Don't resample at first. Just get us to Float32 format. */
/* !!! FIXME: convert to int32 on devices without hardware float. */
2017-01-06 11:16:26 +01:00
if ( SDL_BuildAudioCVT ( & retval - > cvt_before_resampling , src_format , src_channels , src_rate , AUDIO_F32SYS , pre_resample_channels , src_rate ) < 0 ) {
SDL_FreeAudioStream ( retval ) ;
2017-01-06 01:29:38 +01:00
return NULL ; /* SDL_BuildAudioCVT should have called SDL_SetError. */
}
2017-01-07 05:43:53 +01:00
# ifdef HAVE_LIBSAMPLERATE_H
2017-01-06 11:16:26 +01:00
SetupLibSampleRateResampling ( retval ) ;
# endif
if ( ! retval - > resampler_func ) {
retval - > resampler_state = SDL_calloc ( 1 , sizeof ( SDL_AudioStreamResamplerState ) ) ;
if ( ! retval - > resampler_state ) {
SDL_FreeAudioStream ( retval ) ;
SDL_OutOfMemory ( ) ;
return NULL ;
}
2017-09-21 08:51:14 +02:00
if ( SDL_PrepareResampleFilter ( ) < 0 ) {
SDL_free ( retval - > resampler_state ) ;
retval - > resampler_state = NULL ;
SDL_FreeAudioStream ( retval ) ;
return NULL ;
}
2017-01-06 11:16:26 +01:00
retval - > resampler_func = SDL_ResampleAudioStream ;
retval - > reset_resampler_func = SDL_ResetAudioStreamResampler ;
retval - > cleanup_resampler_func = SDL_CleanupAudioStreamResampler ;
}
2017-01-06 01:29:38 +01:00
/* Convert us to the final format after resampling. */
2017-01-06 11:16:26 +01:00
if ( SDL_BuildAudioCVT ( & retval - > cvt_after_resampling , AUDIO_F32SYS , pre_resample_channels , dst_rate , dst_format , dst_channels , dst_rate ) < 0 ) {
SDL_FreeAudioStream ( retval ) ;
2017-01-06 01:29:38 +01:00
return NULL ; /* SDL_BuildAudioCVT should have called SDL_SetError. */
}
}
retval - > queue = SDL_NewDataQueue ( packetlen , packetlen * 2 ) ;
if ( ! retval - > queue ) {
2017-01-06 11:16:26 +01:00
SDL_FreeAudioStream ( retval ) ;
2017-01-06 01:29:38 +01:00
return NULL ; /* SDL_NewDataQueue should have called SDL_SetError. */
}
return retval ;
}
int
SDL_AudioStreamPut ( SDL_AudioStream * stream , const void * buf , const Uint32 _buflen )
{
int buflen = ( int ) _buflen ;
2017-01-24 16:09:29 +01:00
const void * origbuf = buf ;
2017-01-06 01:29:38 +01:00
2017-01-24 06:51:33 +01:00
/* !!! FIXME: several converters can take advantage of SIMD, but only
! ! ! FIXME : if the data is aligned to 16 bytes . EnsureStreamBufferSize ( )
! ! ! FIXME : guarantees the buffer will align , but the
! ! ! FIXME : converters will iterate over the data backwards if
! ! ! FIXME : the output grows , and this means we won ' t align if buflen
! ! ! FIXME : isn ' t a multiple of 16. In these cases , we should chop off
! ! ! FIXME : a few samples at the end and convert them separately . */
2017-01-06 01:29:38 +01:00
if ( ! stream ) {
return SDL_InvalidParamError ( " stream " ) ;
} else if ( ! buf ) {
return SDL_InvalidParamError ( " buf " ) ;
} else if ( buflen = = 0 ) {
return 0 ; /* nothing to do. */
} else if ( ( buflen % stream - > src_sample_frame_size ) ! = 0 ) {
return SDL_SetError ( " Can't add partial sample frames " ) ;
}
if ( stream - > cvt_before_resampling . needed ) {
const int workbuflen = buflen * stream - > cvt_before_resampling . len_mult ; /* will be "* 1" if not needed */
2017-01-24 06:51:33 +01:00
Uint8 * workbuf = EnsureStreamBufferSize ( stream , workbuflen ) ;
2017-01-06 01:29:38 +01:00
if ( workbuf = = NULL ) {
return - 1 ; /* probably out of memory. */
}
2017-01-24 16:09:29 +01:00
SDL_assert ( buf = = origbuf ) ;
2017-01-06 01:29:38 +01:00
SDL_memcpy ( workbuf , buf , buflen ) ;
stream - > cvt_before_resampling . buf = workbuf ;
stream - > cvt_before_resampling . len = buflen ;
if ( SDL_ConvertAudio ( & stream - > cvt_before_resampling ) = = - 1 ) {
return - 1 ; /* uhoh! */
}
buf = workbuf ;
buflen = stream - > cvt_before_resampling . len_cvt ;
}
if ( stream - > dst_rate ! = stream - > src_rate ) {
const int workbuflen = buflen * ( ( int ) SDL_ceil ( stream - > rate_incr ) ) ;
2017-01-24 06:51:33 +01:00
Uint8 * workbuf = EnsureStreamBufferSize ( stream , workbuflen ) ;
2017-01-06 01:29:38 +01:00
if ( workbuf = = NULL ) {
return - 1 ; /* probably out of memory. */
}
2017-01-25 02:30:48 +01:00
/* don't SDL_memcpy(workbuf, buf, buflen) here; our resampler can work inplace or not,
libsamplerate needs buffers to be separate ; either way , avoid a copy here if possible . */
if ( buf ! = origbuf ) {
buf = workbuf ; /* in case we realloc()'d the pointer. */
2017-01-24 06:17:40 +01:00
}
2017-01-25 02:30:48 +01:00
buflen = stream - > resampler_func ( stream , buf , buflen , workbuf , workbuflen ) ;
buf = EnsureStreamBufferSize ( stream , workbuflen ) ;
SDL_assert ( buf ! = NULL ) ; /* shouldn't be growing, just aligning. */
2017-01-06 01:29:38 +01:00
}
if ( stream - > cvt_after_resampling . needed ) {
2017-01-24 06:08:24 +01:00
const int workbuflen = buflen * stream - > cvt_after_resampling . len_mult ; /* will be "* 1" if not needed */
2017-01-24 06:51:33 +01:00
Uint8 * workbuf = EnsureStreamBufferSize ( stream , workbuflen ) ;
2017-01-06 01:29:38 +01:00
if ( workbuf = = NULL ) {
return - 1 ; /* probably out of memory. */
}
2017-01-24 16:09:29 +01:00
if ( buf = = origbuf ) { /* copy if we haven't before. */
2017-07-05 18:04:37 +02:00
SDL_memcpy ( workbuf , origbuf , buflen ) ;
2017-01-24 06:17:40 +01:00
}
2017-01-06 01:29:38 +01:00
stream - > cvt_after_resampling . buf = workbuf ;
stream - > cvt_after_resampling . len = buflen ;
if ( SDL_ConvertAudio ( & stream - > cvt_after_resampling ) = = - 1 ) {
return - 1 ; /* uhoh! */
}
buf = workbuf ;
buflen = stream - > cvt_after_resampling . len_cvt ;
}
return SDL_WriteToDataQueue ( stream - > queue , buf , buflen ) ;
}
void
SDL_AudioStreamClear ( SDL_AudioStream * stream )
{
if ( ! stream ) {
SDL_InvalidParamError ( " stream " ) ;
} else {
SDL_ClearDataQueue ( stream - > queue , stream - > packetlen * 2 ) ;
2017-01-07 03:23:51 +01:00
if ( stream - > reset_resampler_func ) {
stream - > reset_resampler_func ( stream ) ;
}
2017-01-06 01:29:38 +01:00
}
}
/* get converted/resampled data from the stream */
int
2017-01-06 07:02:58 +01:00
SDL_AudioStreamGet ( SDL_AudioStream * stream , void * buf , const Uint32 len )
2017-01-06 01:29:38 +01:00
{
if ( ! stream ) {
return SDL_InvalidParamError ( " stream " ) ;
} else if ( ! buf ) {
return SDL_InvalidParamError ( " buf " ) ;
} else if ( len = = 0 ) {
return 0 ; /* nothing to do. */
} else if ( ( len % stream - > dst_sample_frame_size ) ! = 0 ) {
return SDL_SetError ( " Can't request partial sample frames " ) ;
}
2017-01-06 07:02:58 +01:00
return ( int ) SDL_ReadFromDataQueue ( stream - > queue , buf , len ) ;
2017-01-06 01:29:38 +01:00
}
/* number of converted/resampled bytes available */
int
SDL_AudioStreamAvailable ( SDL_AudioStream * stream )
{
return stream ? ( int ) SDL_CountDataQueue ( stream - > queue ) : 0 ;
}
/* dispose of a stream */
void
SDL_FreeAudioStream ( SDL_AudioStream * stream )
{
if ( stream ) {
2017-01-06 11:16:26 +01:00
if ( stream - > cleanup_resampler_func ) {
stream - > cleanup_resampler_func ( stream ) ;
}
2017-01-06 01:29:38 +01:00
SDL_FreeDataQueue ( stream - > queue ) ;
2017-01-24 06:51:33 +01:00
SDL_free ( stream - > work_buffer_base ) ;
2017-01-06 01:29:38 +01:00
SDL_free ( stream ) ;
}
}
2015-06-21 17:33:46 +02:00
/* vi: set ts=4 sw=4 expandtab: */
2016-11-05 07:34:38 +01:00