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-10-20 23:51:22 +02:00
/* 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
*/
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-10-10 22:12:56 +02:00
# define DEBUG_AUDIOSTREAM 0
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-09-22 13:42:24 +02:00
static int
ResamplerPadding ( const int inrate , const int outrate )
{
2017-10-10 22:12:56 +02:00
if ( inrate = = outrate ) {
return 0 ;
} else if ( inrate > outrate ) {
return ( int ) SDL_ceil ( ( ( float ) ( RESAMPLER_SAMPLES_PER_ZERO_CROSSING * inrate ) / ( ( float ) outrate ) ) ) ;
}
return RESAMPLER_SAMPLES_PER_ZERO_CROSSING ;
2017-09-22 13:42:24 +02:00
}
2017-01-23 02:27:48 +01:00
2017-09-22 13:42:24 +02:00
/* lpadding and rpadding are expected to be buffers of (ResamplePadding(inrate, outrate) * chans * sizeof (float)) bytes. */
2017-09-21 08:51:14 +02:00
static int
SDL_ResampleAudio ( const int chans , const int inrate , const int outrate ,
2017-10-10 22:12:56 +02:00
const float * lpadding , const float * rpadding ,
const float * inbuf , const int inbuflen ,
float * outbuf , const int outbuflen )
2017-09-21 08:51:14 +02:00
{
2017-10-11 08:31:58 +02:00
const double finrate = ( double ) inrate ;
2017-10-11 17:51:14 +02:00
const double outtimeincr = 1.0 / ( ( float ) outrate ) ;
const double ratio = ( ( float ) outrate ) / ( ( float ) inrate ) ;
2017-09-22 13:42:24 +02:00
const int paddinglen = ResamplerPadding ( inrate , outrate ) ;
2017-09-21 08:51:14 +02:00
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 ;
2017-10-10 22:12:56 +02:00
const int outframes = SDL_min ( wantedoutframes , maxoutframes ) ;
2017-09-21 08:51:14 +02:00
float * dst = outbuf ;
2017-10-11 17:51:14 +02:00
double outtime = 0.0 ;
2017-09-21 08:51:14 +02:00
int i , j , chan ;
for ( i = 0 ; i < outframes ; i + + ) {
const int srcindex = ( int ) ( outtime * inrate ) ;
2017-10-11 17:51:14 +02:00
const double intime = ( ( double ) srcindex ) / finrate ;
const double innexttime = ( ( double ) ( srcindex + 1 ) ) / finrate ;
const double interpolation1 = 1.0 - ( ( innexttime - outtime ) / ( innexttime - intime ) ) ;
2017-09-21 08:51:14 +02:00
const int filterindex1 = ( int ) ( interpolation1 * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ;
2017-10-11 17:51:14 +02:00
const double interpolation2 = 1.0 - interpolation1 ;
2017-09-23 04:28:21 +02:00
const int filterindex2 = ( int ) ( interpolation2 * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ;
2017-09-21 08:51:14 +02:00
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 + + ) {
const int srcframe = srcindex - j ;
2017-09-22 13:42:24 +02:00
/* !!! FIXME: we can bubble this conditional out of here by doing a pre loop. */
const float insample = ( srcframe < 0 ) ? lpadding [ ( ( paddinglen + srcframe ) * chans ) + chan ] : inbuf [ ( srcframe * chans ) + chan ] ;
2017-10-12 22:55:35 +02:00
outsample + = ( float ) ( insample * ( ResamplerFilter [ filterindex1 + ( j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ] + ( interpolation1 * ResamplerFilterDifference [ filterindex1 + ( j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ] ) ) ) ;
2017-09-21 08:51:14 +02:00
}
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 ;
2017-09-22 13:42:24 +02:00
/* !!! FIXME: we can bubble this conditional out of here by doing a post loop. */
const float insample = ( srcframe > = inframes ) ? rpadding [ ( ( srcframe - inframes ) * chans ) + chan ] : inbuf [ ( srcframe * chans ) + chan ] ;
2017-10-12 22:55:35 +02:00
outsample + = ( float ) ( insample * ( ResamplerFilter [ filterindex2 + ( j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ] + ( interpolation2 * ResamplerFilterDifference [ filterindex2 + ( j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING ) ] ) ) ) ;
2017-09-21 08:51:14 +02:00
}
* ( dst + + ) = outsample ;
}
2017-10-11 18:07:43 +02:00
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 . : ( */
2017-09-22 13:42:24 +02:00
const int inrate = ( int ) ( size_t ) cvt - > filters [ SDL_AUDIOCVT_MAX_FILTERS - 1 ] ;
const int outrate = ( 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-09-22 13:42:24 +02:00
const int paddingsamples = ( ResamplerPadding ( inrate , outrate ) * chans ) ;
2017-09-22 17:51:45 +02:00
float * padding ;
2017-01-09 12:00:58 +01:00
SDL_assert ( format = = AUDIO_F32SYS ) ;
2017-09-22 13:42:24 +02:00
/* we keep no streaming state here, so pad with silence on both ends. */
2017-10-11 04:18:46 +02:00
padding = ( float * ) SDL_calloc ( paddingsamples , sizeof ( float ) ) ;
2017-09-22 17:51:45 +02:00
if ( ! padding ) {
SDL_OutOfMemory ( ) ;
return ;
}
2017-09-21 08:51:14 +02:00
2017-09-22 13:42:24 +02:00
cvt - > len_cvt = SDL_ResampleAudio ( chans , inrate , outrate , padding , padding , src , srclen , dst , dstlen ) ;
2017-09-21 08:51:14 +02:00
2017-10-11 04:18:46 +02:00
SDL_free ( padding ) ;
2017-09-22 17:51:45 +02:00
2017-10-11 04:31:02 +02:00
SDL_memmove ( 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
2017-10-19 00:54:05 +02:00
struct _SDL_AudioStream
2017-01-06 01:29:38 +01:00
{
SDL_AudioCVT cvt_before_resampling ;
SDL_AudioCVT cvt_after_resampling ;
SDL_DataQueue * queue ;
2017-10-10 22:12:56 +02:00
SDL_bool first_run ;
2017-10-19 04:26:36 +02:00
Uint8 * staging_buffer ;
int staging_buffer_size ;
int staging_buffer_filled ;
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-10-10 22:12:56 +02:00
int resampler_padding_samples ;
float * resampler_padding ;
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-10-10 22:12:56 +02:00
SDL_assert ( inbuf ! = ( ( const float * ) outbuf ) ) ; /* SDL_AudioStreamPut() shouldn't allow in-place resamples. */
2017-01-25 02:30:48 +01:00
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
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-10-10 22:12:56 +02:00
const Uint8 * inbufend = ( ( const Uint8 * ) _inbuf ) + inbuflen ;
2017-01-24 06:08:24 +01:00
const float * inbuf = ( const float * ) _inbuf ;
float * outbuf = ( float * ) _outbuf ;
2017-09-22 13:42:24 +02:00
const int chans = ( int ) stream - > pre_resample_channels ;
const int inrate = stream - > src_rate ;
const int outrate = stream - > dst_rate ;
2017-10-10 22:12:56 +02:00
const int paddingsamples = stream - > resampler_padding_samples ;
2017-09-22 13:42:24 +02:00
const int paddingbytes = paddingsamples * sizeof ( float ) ;
float * lpadding = ( float * ) stream - > resampler_state ;
2017-10-10 22:12:56 +02:00
const float * rpadding = ( const float * ) inbufend ; /* we set this up so there are valid padding samples at the end of the input buffer. */
2017-10-11 08:03:05 +02:00
const int cpy = SDL_min ( inbuflen , paddingbytes ) ;
2017-09-22 13:42:24 +02:00
int retval ;
2017-01-06 11:16:26 +01:00
2017-10-10 22:12:56 +02:00
SDL_assert ( inbuf ! = ( ( const float * ) outbuf ) ) ; /* SDL_AudioStreamPut() shouldn't allow in-place resamples. */
2017-09-22 17:51:45 +02:00
2017-09-22 13:42:24 +02:00
retval = SDL_ResampleAudio ( chans , inrate , outrate , lpadding , rpadding , inbuf , inbuflen , outbuf , outbuflen ) ;
2017-01-24 06:08:24 +01:00
2017-09-22 13:42:24 +02:00
/* update our left padding with end of current input, for next run. */
2017-10-11 08:03:05 +02:00
SDL_memcpy ( ( lpadding + paddingsamples ) - ( cpy / sizeof ( float ) ) , inbufend - cpy , cpy ) ;
2017-09-22 13:42:24 +02:00
return retval ;
2017-01-06 11:16:26 +01:00
}
static void
SDL_ResetAudioStreamResampler ( SDL_AudioStream * stream )
{
2017-10-10 22:12:56 +02:00
/* set all the padding to silence. */
const int len = stream - > resampler_padding_samples ;
2017-09-22 13:42:24 +02:00
SDL_memset ( stream - > resampler_state , ' \0 ' , len * sizeof ( float ) ) ;
2017-01-06 11:16:26 +01:00
}
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-10-10 22:12:56 +02:00
retval - > first_run = SDL_TRUE ;
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 ) ;
2017-10-10 22:12:56 +02:00
retval - > resampler_padding_samples = ResamplerPadding ( retval - > src_rate , retval - > dst_rate ) * pre_resample_channels ;
retval - > resampler_padding = ( float * ) SDL_calloc ( retval - > resampler_padding_samples , sizeof ( float ) ) ;
if ( retval - > resampler_padding = = NULL ) {
SDL_FreeAudioStream ( retval ) ;
SDL_OutOfMemory ( ) ;
return NULL ;
}
2017-01-06 01:29:38 +01:00
2017-10-19 04:26:36 +02:00
retval - > staging_buffer_size = ( ( retval - > resampler_padding_samples / retval - > pre_resample_channels ) * retval - > src_sample_frame_size ) ;
if ( retval - > staging_buffer_size > 0 ) {
retval - > staging_buffer = ( Uint8 * ) SDL_malloc ( retval - > staging_buffer_size ) ;
2017-10-19 05:49:46 +02:00
if ( retval - > staging_buffer = = NULL ) {
2017-10-19 04:26:36 +02:00
SDL_FreeAudioStream ( retval ) ;
SDL_OutOfMemory ( ) ;
return NULL ;
}
}
/* Not resampling? It's an easy conversion (and maybe not even that!) */
2017-01-06 01:29:38 +01:00
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 ) {
2017-10-10 22:12:56 +02:00
retval - > resampler_state = SDL_calloc ( retval - > resampler_padding_samples , sizeof ( float ) ) ;
2017-01-06 11:16:26 +01:00
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 ;
}
2017-10-19 04:26:36 +02:00
static int
2017-10-20 00:05:42 +02:00
SDL_AudioStreamPutInternal ( SDL_AudioStream * stream , const void * buf , int len , int * maxputbytes )
2017-01-06 01:29:38 +01:00
{
2017-10-19 00:54:05 +02:00
int buflen = len ;
2017-10-10 22:12:56 +02:00
int workbuflen ;
Uint8 * workbuf ;
Uint8 * resamplebuf = NULL ;
int resamplebuflen = 0 ;
2017-10-11 07:37:11 +02:00
int neededpaddingbytes ;
2017-10-10 22:12:56 +02:00
int paddingbytes ;
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-10-10 22:12:56 +02:00
/* no padding prepended on first run. */
2017-10-11 07:37:11 +02:00
neededpaddingbytes = stream - > resampler_padding_samples * sizeof ( float ) ;
2017-10-10 22:12:56 +02:00
paddingbytes = stream - > first_run ? 0 : neededpaddingbytes ;
stream - > first_run = SDL_FALSE ;
/* Make sure the work buffer can hold all the data we need at once... */
workbuflen = buflen ;
2017-01-06 01:29:38 +01:00
if ( stream - > cvt_before_resampling . needed ) {
2017-10-10 22:12:56 +02:00
workbuflen * = stream - > cvt_before_resampling . len_mult ;
}
if ( stream - > dst_rate ! = stream - > src_rate ) {
/* resamples can't happen in place, so make space for second buf. */
const int framesize = stream - > pre_resample_channels * sizeof ( float ) ;
const int frames = workbuflen / framesize ;
resamplebuflen = ( ( int ) SDL_ceil ( frames * stream - > rate_incr ) ) * framesize ;
# if DEBUG_AUDIOSTREAM
printf ( " AUDIOSTREAM: will resample %d bytes to %d (ratio=%.6f) \n " , workbuflen , resamplebuflen , stream - > rate_incr ) ;
# endif
workbuflen + = resamplebuflen ;
}
if ( stream - > cvt_after_resampling . needed ) {
/* !!! FIXME: buffer might be big enough already? */
workbuflen * = stream - > cvt_after_resampling . len_mult ;
}
workbuflen + = neededpaddingbytes ;
# if DEBUG_AUDIOSTREAM
printf ( " AUDIOSTREAM: Putting %d bytes of preconverted audio, need %d byte work buffer \n " , buflen , workbuflen ) ;
# endif
workbuf = EnsureStreamBufferSize ( stream , workbuflen ) ;
if ( ! workbuf ) {
return - 1 ; /* probably out of memory. */
}
resamplebuf = workbuf ; /* default if not resampling. */
SDL_memcpy ( workbuf + paddingbytes , buf , buflen ) ;
if ( stream - > cvt_before_resampling . needed ) {
stream - > cvt_before_resampling . buf = workbuf + paddingbytes ;
2017-01-06 01:29:38 +01:00
stream - > cvt_before_resampling . len = buflen ;
if ( SDL_ConvertAudio ( & stream - > cvt_before_resampling ) = = - 1 ) {
return - 1 ; /* uhoh! */
}
buflen = stream - > cvt_before_resampling . len_cvt ;
2017-10-10 22:12:56 +02:00
# if DEBUG_AUDIOSTREAM
printf ( " AUDIOSTREAM: After initial conversion we have %d bytes \n " , buflen ) ;
# endif
2017-01-06 01:29:38 +01:00
}
if ( stream - > dst_rate ! = stream - > src_rate ) {
2017-10-10 22:12:56 +02:00
/* save off some samples at the end; they are used for padding now so
the resampler is coherent and then used at the start of the next
put operation . Prepend last put operation ' s padding , too . */
/* prepend prior put's padding. :P */
if ( paddingbytes ) {
SDL_memcpy ( workbuf , stream - > resampler_padding , paddingbytes ) ;
buflen + = paddingbytes ;
2017-01-06 01:29:38 +01:00
}
2017-10-10 22:12:56 +02:00
/* save off the data at the end for the next run. */
SDL_memcpy ( stream - > resampler_padding , workbuf + ( buflen - neededpaddingbytes ) , neededpaddingbytes ) ;
resamplebuf = workbuf + buflen ; /* skip to second piece of workbuf. */
2017-10-11 08:03:05 +02:00
SDL_assert ( buflen > = neededpaddingbytes ) ;
if ( buflen > neededpaddingbytes ) {
buflen = stream - > resampler_func ( stream , workbuf , buflen - neededpaddingbytes , resamplebuf , resamplebuflen ) ;
} else {
buflen = 0 ;
}
2017-10-10 22:12:56 +02:00
# if DEBUG_AUDIOSTREAM
printf ( " AUDIOSTREAM: After resampling we have %d bytes \n " , buflen ) ;
# endif
2017-01-06 01:29:38 +01:00
}
2017-10-11 08:03:05 +02:00
if ( stream - > cvt_after_resampling . needed & & ( buflen > 0 ) ) {
2017-10-10 22:12:56 +02:00
stream - > cvt_after_resampling . buf = resamplebuf ;
2017-01-06 01:29:38 +01:00
stream - > cvt_after_resampling . len = buflen ;
if ( SDL_ConvertAudio ( & stream - > cvt_after_resampling ) = = - 1 ) {
return - 1 ; /* uhoh! */
}
buflen = stream - > cvt_after_resampling . len_cvt ;
2017-10-10 22:12:56 +02:00
# if DEBUG_AUDIOSTREAM
printf ( " AUDIOSTREAM: After final conversion we have %d bytes \n " , buflen ) ;
# endif
2017-01-06 01:29:38 +01:00
}
2017-10-10 22:12:56 +02:00
# if DEBUG_AUDIOSTREAM
printf ( " AUDIOSTREAM: Final output is %d bytes \n " , buflen ) ;
# endif
2017-10-20 00:05:42 +02:00
if ( maxputbytes ) {
const int maxbytes = * maxputbytes ;
if ( buflen > maxbytes )
buflen = maxbytes ;
* maxputbytes - = buflen ;
}
2017-10-10 22:12:56 +02:00
/* resamplebuf holds the final output, even if we didn't resample. */
2017-10-11 08:03:05 +02:00
return buflen ? SDL_WriteToDataQueue ( stream - > queue , resamplebuf , buflen ) : 0 ;
2017-01-06 01:29:38 +01:00
}
2017-10-19 04:26:36 +02:00
int
SDL_AudioStreamPut ( SDL_AudioStream * stream , const void * buf , int len )
{
/* !!! 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 . */
# if DEBUG_AUDIOSTREAM
printf ( " AUDIOSTREAM: wants to put %d preconverted bytes \n " , buflen ) ;
# endif
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 - > src_sample_frame_size ) ! = 0 ) {
return SDL_SetError ( " Can't add partial sample frames " ) ;
}
if ( ! stream - > cvt_before_resampling . needed & &
( stream - > dst_rate = = stream - > src_rate ) & &
! stream - > cvt_after_resampling . needed ) {
# if DEBUG_AUDIOSTREAM
printf ( " AUDIOSTREAM: no conversion needed at all, queueing %d bytes. \n " , len ) ;
# endif
return SDL_WriteToDataQueue ( stream - > queue , buf , len ) ;
}
while ( len > 0 ) {
int amount ;
/* If we don't have a staging buffer or we're given enough data that
we don ' t need to store it for later , skip the staging process .
*/
if ( ! stream - > staging_buffer_filled & & len > = stream - > staging_buffer_size ) {
2017-10-20 00:05:42 +02:00
return SDL_AudioStreamPutInternal ( stream , buf , len , NULL ) ;
2017-10-19 04:26:36 +02:00
}
/* If there's not enough data to fill the staging buffer, just save it */
if ( ( stream - > staging_buffer_filled + len ) < stream - > staging_buffer_size ) {
SDL_memcpy ( stream - > staging_buffer + stream - > staging_buffer_filled , buf , len ) ;
stream - > staging_buffer_filled + = len ;
return 0 ;
}
/* Fill the staging buffer, process it, and continue */
amount = ( stream - > staging_buffer_size - stream - > staging_buffer_filled ) ;
SDL_assert ( amount > 0 ) ;
SDL_memcpy ( stream - > staging_buffer + stream - > staging_buffer_filled , buf , amount ) ;
stream - > staging_buffer_filled = 0 ;
2017-10-20 00:05:42 +02:00
if ( SDL_AudioStreamPutInternal ( stream , stream - > staging_buffer , stream - > staging_buffer_size , NULL ) < 0 ) {
2017-10-19 04:26:36 +02:00
return - 1 ;
}
buf = ( void * ) ( ( Uint8 * ) buf + amount ) ;
len - = amount ;
}
return 0 ;
}
2017-10-20 00:05:42 +02:00
int SDL_AudioStreamFlush ( SDL_AudioStream * stream )
{
if ( ! stream ) {
return SDL_InvalidParamError ( " stream " ) ;
}
# if DEBUG_AUDIOSTREAM
printf ( " AUDIOSTREAM: flushing! staging_buffer_filled=%d bytes \n " , stream - > staging_buffer_filled ) ;
# endif
/* shouldn't use a staging buffer if we're not resampling. */
SDL_assert ( ( stream - > dst_rate ! = stream - > src_rate ) | | ( stream - > staging_buffer_filled = = 0 ) ) ;
if ( stream - > staging_buffer_filled > 0 ) {
/* push the staging buffer + silence. We need to flush out not just
the staging buffer , but the piece that the stream was saving off
for right - side resampler padding . */
const SDL_bool first_run = stream - > first_run ;
const int filled = stream - > staging_buffer_filled ;
int actual_input_frames = filled / stream - > src_sample_frame_size ;
if ( ! first_run )
actual_input_frames + = stream - > resampler_padding_samples / stream - > pre_resample_channels ;
if ( actual_input_frames > 0 ) { /* don't bother if nothing to flush. */
/* This is how many bytes we're expecting without silence appended. */
int flush_remaining = ( ( int ) SDL_ceil ( actual_input_frames * stream - > rate_incr ) ) * stream - > dst_sample_frame_size ;
# if DEBUG_AUDIOSTREAM
printf ( " AUDIOSTREAM: flushing with padding to get max %d bytes! \n " , flush_remaining ) ;
# endif
SDL_memset ( stream - > staging_buffer + filled , ' \0 ' , stream - > staging_buffer_size - filled ) ;
if ( SDL_AudioStreamPutInternal ( stream , stream - > staging_buffer , stream - > staging_buffer_size , & flush_remaining ) < 0 ) {
return - 1 ;
}
/* we have flushed out (or initially filled) the pending right-side
resampler padding , but we need to push more silence to guarantee
the staging buffer is fully flushed out , too . */
SDL_memset ( stream - > staging_buffer , ' \0 ' , filled ) ;
if ( SDL_AudioStreamPutInternal ( stream , stream - > staging_buffer , stream - > staging_buffer_size , & flush_remaining ) < 0 ) {
return - 1 ;
}
}
}
stream - > staging_buffer_filled = 0 ;
stream - > first_run = SDL_TRUE ;
return 0 ;
}
2017-01-06 01:29:38 +01:00
/* get converted/resampled data from the stream */
int
2017-10-19 00:54:05 +02:00
SDL_AudioStreamGet ( SDL_AudioStream * stream , void * buf , int len )
2017-01-06 01:29:38 +01:00
{
2017-10-10 22:12:56 +02:00
# if DEBUG_AUDIOSTREAM
2017-10-19 00:54:05 +02:00
printf ( " AUDIOSTREAM: want to get %d converted bytes \n " , len ) ;
2017-10-10 22:12:56 +02:00
# endif
2017-01-06 01:29:38 +01:00
if ( ! stream ) {
return SDL_InvalidParamError ( " stream " ) ;
} else if ( ! buf ) {
return SDL_InvalidParamError ( " buf " ) ;
2017-10-19 00:54:05 +02:00
} else if ( len < = 0 ) {
2017-01-06 01:29:38 +01:00
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 ;
}
2017-10-19 00:54:05 +02:00
void
SDL_AudioStreamClear ( SDL_AudioStream * stream )
{
if ( ! stream ) {
SDL_InvalidParamError ( " stream " ) ;
} else {
SDL_ClearDataQueue ( stream - > queue , stream - > packetlen * 2 ) ;
if ( stream - > reset_resampler_func ) {
stream - > reset_resampler_func ( stream ) ;
}
stream - > first_run = SDL_TRUE ;
2017-10-20 00:05:42 +02:00
stream - > staging_buffer_filled = 0 ;
2017-10-19 00:54:05 +02:00
}
}
2017-01-06 01:29:38 +01:00
/* 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-10-19 04:26:36 +02:00
SDL_free ( stream - > staging_buffer ) ;
2017-01-24 06:51:33 +01:00
SDL_free ( stream - > work_buffer_base ) ;
2017-10-10 22:12:56 +02:00
SDL_free ( stream - > resampler_padding ) ;
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