From acfdfa9eda1c961b78db9391ade26ec23af9f174 Mon Sep 17 00:00:00 2001 From: avolkov Date: Wed, 5 Feb 2003 03:43:51 +0000 Subject: [PATCH] MixSDL mixer: code cleanup; resampling fixes; mixing quality improvements (smoothed-out clipping) git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@677 8092fc87-c524-0410-9efc-e669fe64eaf9 --- sc2/src/sc2code/libs/sound/mixsdl/mixer.c | 277 +++++++++++++++---- sc2/src/sc2code/libs/sound/mixsdl/mixer.h | 50 +--- sc2/src/sc2code/libs/sound/mixsdl/mixerint.h | 125 +++++++++ 3 files changed, 345 insertions(+), 107 deletions(-) create mode 100644 sc2/src/sc2code/libs/sound/mixsdl/mixerint.h diff --git a/sc2/src/sc2code/libs/sound/mixsdl/mixer.c b/sc2/src/sc2code/libs/sound/mixsdl/mixer.c index 3b2d5b69c..445204ac6 100644 --- a/sc2/src/sc2code/libs/sound/mixsdl/mixer.c +++ b/sc2/src/sc2code/libs/sound/mixsdl/mixer.c @@ -29,6 +29,7 @@ #include "SDL_byteorder.h" #include "SDL_audio.h" #include "SDL_version.h" +#include "mixerint.h" #define DEBUG @@ -41,16 +42,9 @@ static uint32 mixer_sampsize; static sint32 mixer_silence; static uint32 mixer_freq; -/* this is purely private */ -/* Reentrant mutex */ -typedef struct -{ - Semaphore sem; - uint32 thread_id; - uint32 locks; - -} mixSDL_Mutex_info; -typedef mixSDL_Mutex_info *mixSDL_Mutex; +/* mixer interim work-buffer; size in one-channel samples */ +static uint32 mixer_datasize; +static sint32 *mixer_data = 0; mixSDL_Mutex_info mixer_mutexes[3]; /* when locking more than one mutex @@ -63,28 +57,6 @@ static mixSDL_Mutex act_mutex = mixer_mutexes + 2; #define MAX_SOURCES 8 mixSDL_Source *active_sources[MAX_SOURCES]; -static void mixSDL_mix_channels (void *userdata, uint8 *stream, - sint32 len); -static void mixSDL_ConvertBuffer_internal (mixSDL_Convertion *conv); -static void mixSDL_Resample_frac (mixSDL_Convertion *conv); -static void mixSDL_ResampleFlat (mixSDL_Convertion *conv); -static void mixSDL_ResampleUp_fast (mixSDL_Convertion *conv); -static void mixSDL_ResampleDown_fast (mixSDL_Convertion *conv); -static void mixSDL_SourceUnqueueAll (mixSDL_Source *src); -static void mixSDL_SourceStop_internal (mixSDL_Source *src); -static void mixSDL_SourceActivate (mixSDL_Source* src); -static void mixSDL_SourceDeactivate (mixSDL_Source* src); - -static __inline__ bool mixSDL_SourceGetNextSample (mixSDL_Source *src, - sint32* samp); - -static __inline__ sint32 mixSDL_GetSampleExt (void *src, uint32 bpc); -static __inline__ sint32 mixSDL_GetSampleInt (void *src, uint32 bpc); -static __inline__ void mixSDL_PutSampleInt (void *dst, uint32 bpc, - sint32 samp); -static __inline__ void mixSDL_PutSampleExt (void *dst, uint32 bpc, - sint32 samp); - /************************************************* * Internals @@ -160,7 +132,7 @@ mixSDL_GetError (void) /* Open the mixer with a certain desired audio format */ bool -mixSDL_OpenAudio (uint32 frequency, uint32 format, uint32 chunksize) +mixSDL_OpenAudio (uint32 frequency, uint32 format, uint32 samples_buf) { SDL_AudioSpec desired; @@ -188,7 +160,7 @@ mixSDL_OpenAudio (uint32 frequency, uint32 format, uint32 chunksize) /* Set the desired format and frequency */ desired.freq = frequency; - desired.samples = chunksize; + desired.samples = samples_buf; desired.callback = mixSDL_mix_channels; desired.userdata = NULL; @@ -227,6 +199,10 @@ mixSDL_OpenAudio (uint32 frequency, uint32 format, uint32 chunksize) mixer_sampsize = mixer_chansize * mixer_spec.channels; mixer_freq = mixer_spec.freq; + /* 2x size the sound playback buffer should be enough for anything */ + mixer_datasize = samples_buf * mixer_spec.channels * 2; + mixer_data = (sint32 *) HMalloc (sizeof (uint32) * mixer_datasize); + mixSDL_InitMutex (src_mutex, "mixSDL_SourceMutex"); mixSDL_InitMutex (buf_mutex, "mixSDL_BufferMutex"); mixSDL_InitMutex (act_mutex, "mixSDL_ActiveMutex"); @@ -247,6 +223,12 @@ mixSDL_CloseAudio (void) { SDL_CloseAudio(); + if (mixer_data) + { + HFree (mixer_data); + mixer_data = 0; + } + mixSDL_TermMutex (src_mutex); mixSDL_TermMutex (buf_mutex); mixSDL_TermMutex (act_mutex); @@ -925,7 +907,7 @@ mixSDL_SourceUnqueueBuffers (mixSDL_Object srcobj, uint32 n, * Sources internals */ -void +static void mixSDL_SourceUnqueueAll (mixSDL_Source *src) { mixSDL_Buffer *buf; @@ -967,7 +949,7 @@ mixSDL_SourceUnqueueAll (mixSDL_Source *src) } /* add the source to the active array */ -void +static void mixSDL_SourceActivate (mixSDL_Source* src) { uint32 i; @@ -1006,7 +988,7 @@ mixSDL_SourceActivate (mixSDL_Source* src) } /* remove the source from the active array */ -void +static void mixSDL_SourceDeactivate (mixSDL_Source* src) { uint32 i; @@ -1030,7 +1012,7 @@ mixSDL_SourceDeactivate (mixSDL_Source* src) mixSDL_UnlockMutex (act_mutex); } -void +static void mixSDL_SourceStop_internal (mixSDL_Source *src) { mixSDL_Buffer *buf; @@ -1081,7 +1063,7 @@ mixSDL_SourceStop_internal (mixSDL_Source *src) } /* get the sample next in queue in internal format */ -__inline__ bool +static __inline__ bool mixSDL_SourceGetNextSample (mixSDL_Source *src, sint32* psamp) { while (src->nextqueued) @@ -1544,7 +1526,7 @@ mixSDL_ConvertBuffer (uint32 srcfmt, void* srcdata, uint32 srcsize, * Buffer internals */ -void +static void mixSDL_ConvertBuffer_internal (mixSDL_Convertion *conv) { conv->srcbpc = MIX_FORMAT_BPC (conv->srcfmt); @@ -1589,7 +1571,7 @@ mixSDL_ConvertBuffer_internal (mixSDL_Convertion *conv) /* get a sample from external buffer * in internal format */ -__inline__ sint32 +static __inline__ sint32 mixSDL_GetSampleExt (void *src, uint32 bpc) { if (bpc == 2) @@ -1599,7 +1581,7 @@ mixSDL_GetSampleExt (void *src, uint32 bpc) } /* get a sample from internal buffer */ -__inline__ sint32 +static __inline__ sint32 mixSDL_GetSampleInt (void *src, uint32 bpc) { if (bpc == 2) @@ -1611,7 +1593,7 @@ mixSDL_GetSampleInt (void *src, uint32 bpc) /* put a sample into an external buffer * from internal format */ -__inline__ void +static __inline__ void mixSDL_PutSampleExt (void *dst, uint32 bpc, sint32 samp) { if (bpc == 2) @@ -1623,7 +1605,7 @@ mixSDL_PutSampleExt (void *dst, uint32 bpc, sint32 samp) /* put a sample into an internal buffer * in internal format */ -__inline__ void +static __inline__ void mixSDL_PutSampleInt (void *dst, uint32 bpc, sint32 samp) { if (bpc == 2) @@ -1636,7 +1618,7 @@ mixSDL_PutSampleInt (void *dst, uint32 bpc, sint32 samp) * in internal format, while performing * convertion if necessary */ -__inline__ sint32 +static __inline__ sint32 mixSDL_GetConvSample (uint8 **psrc, uint32 bpc, uint32 flags) { register sint32 samp; @@ -1671,7 +1653,7 @@ mixSDL_GetConvSample (uint8 **psrc, uint32 bpc, uint32 flags) * in internal format, while performing * convertion if necessary */ -__inline__ void +static __inline__ void mixSDL_PutConvSample (uint8 **pdst, uint32 bpc, uint32 flags, sint32 samp) { mixSDL_PutSampleInt (*pdst, bpc, samp); @@ -1684,7 +1666,7 @@ mixSDL_PutConvSample (uint8 **pdst, uint32 bpc, uint32 flags, sint32 samp) } /* resampling with fractional rate change */ -void +static void mixSDL_Resample_frac (mixSDL_Convertion *conv) { mixSDL_ConvFlags flags = conv->flags; @@ -1742,7 +1724,13 @@ mixSDL_Resample_frac (mixSDL_Convertion *conv) } else { - nextsampl = nextsampr = 0; + /* theoretically, here we need to get the next sample + * somehow, which is in the next buffer in the stream; + * leaving the next sample equal to the previous should + * do for now; this will create some-20 minimal + * distortions per second + */ + /* nextsampl = nextsampr = 0; */ } } @@ -1760,7 +1748,7 @@ mixSDL_Resample_frac (mixSDL_Convertion *conv) } /* resampling with respect to sample size only */ -void +static void mixSDL_ResampleFlat (mixSDL_Convertion *conv) { mixSDL_ConvFlags flags = conv->flags; @@ -1768,9 +1756,13 @@ mixSDL_ResampleFlat (mixSDL_Convertion *conv) uint8 *dst = conv->dstdata; uint32 srcbpc = conv->srcbpc; uint32 dstbpc = conv->dstbpc; - uint32 i; + uint32 samples; - for (i = conv->srcsamples; i; i--) + samples = conv->srcsamples; + if ( !(conv->flags & (mixConvStereoUp | mixConvStereoDown))) + samples *= conv->srcchans; + + for (; samples; samples--) { register sint32 samp; @@ -1780,7 +1772,7 @@ mixSDL_ResampleFlat (mixSDL_Convertion *conv) } /* integer-periodic resampling with rate increase */ -void +static void mixSDL_ResampleUp_fast (mixSDL_Convertion *conv) { mixSDL_ConvFlags flags = conv->flags; @@ -1814,7 +1806,13 @@ mixSDL_ResampleUp_fast (mixSDL_Convertion *conv) } else { - nextsampl = nextsampr = 0; + /* theoretically, here we need to get the next sample + * somehow, which is in the next buffer in the stream; + * leaving the next sample equal to the previous should + * do for now; this will create some-20 minimal + * distortions per second + */ + /* nextsampl = nextsampr = 0; */ } for (di = convrate; di > 0; di--) @@ -1831,7 +1829,7 @@ mixSDL_ResampleUp_fast (mixSDL_Convertion *conv) } /* integer-periodic resampling with rate decrease */ -void +static void mixSDL_ResampleDown_fast (mixSDL_Convertion *conv) { mixSDL_ConvFlags flags = conv->flags; @@ -1861,13 +1859,175 @@ mixSDL_ResampleDown_fast (mixSDL_Convertion *conv) } -/************************************************* - * THE mixer - suprisingly simple in the end +/********************************************************** + * THE mixer - higher quality; smoothed-out clipping * * This could use some optimization perhaps */ -void mixSDL_mix_channels (void *userdata, uint8 *stream, sint32 len) +static void +mixSDL_mix_channels (void *userdata, uint8 *stream, sint32 len) +{ + uint32 samples = len / mixer_chansize; + sint32 *end_data = mixer_data + samples; + sint32 *data; + uint32 step; + + /* mixer_datasize < samples should not happen ever, but for now.. */ + if (mixer_datasize < samples) + { +#ifdef DEBUG + fprintf (stderr, "mixSDL_mix_channels(): " + "WARNING: work-buffer too small\n"); +#endif + mixSDL_mix_lowq (stream, len); + } + + /* keep this order or die */ + mixSDL_LockMutex (src_mutex); + mixSDL_LockMutex (buf_mutex); + mixSDL_LockMutex (act_mutex); + + /* first, collect data from sources and put into work-buffer */ + for (data = mixer_data; data < end_data; ++data) + { + uint32 i; + sint32 fullsamp; + + fullsamp = 0; + + for (i = 0; i < MAX_SOURCES; i++) + { + mixSDL_Source *src; + sint32 samp; + + /* find next source */ + for (; i < MAX_SOURCES && ( + (src = active_sources[i]) == 0 + || src->state != MIX_PLAYING + || !mixSDL_SourceGetNextSample (src, &samp)); + i++) + ; + + if (i < MAX_SOURCES) + { + /* sample aquired */ + fullsamp += samp; + } + } + + *data = fullsamp; + } + + /* unclip work-buffer */ + step = mixer_spec.channels; + mixSDL_UnclipWorkBuffer (mixer_data, end_data, step); + if (step == 2) + { /* also, for the second channel */ + mixSDL_UnclipWorkBuffer (mixer_data + 1, end_data, step); + } + + /* copy data into driver buffer */ + for (data = mixer_data; data < end_data; ++data) + { + mixSDL_PutSampleExt (stream, mixer_chansize, *data); + stream += mixer_chansize; + } + + /* keep this order or die */ + mixSDL_UnlockMutex (act_mutex); + mixSDL_UnlockMutex (buf_mutex); + mixSDL_UnlockMutex (src_mutex); + + (void)userdata; // satisfying compiler +} + +/* data unclipping - smooth out the areas that need to be clipped */ +static void +mixSDL_UnclipWorkBuffer (sint32 *data, sint32 *end_data, uint32 step) +{ + while (data < end_data) + { + uint32 len; + sint32 extremum; + sint32 *chunk; + sint32 threshold, origin, range_end; + double gain_mult; + sint32 samp = *data; + + if (mixer_chansize == 2) + { /* S16 */ + if (samp < MIX_UNCLIP_S16_MIN) + { + origin = MIX_UNCLIP_S16_MIN; + range_end = -SINT16_MIN; + threshold = -MIX_UNCLIP_S16_MIN; + } + else if (samp > MIX_UNCLIP_S16_MAX) + { + origin = MIX_UNCLIP_S16_MAX; + range_end = SINT16_MAX; + threshold = MIX_UNCLIP_S16_MAX; + } + else + { + data += step; + continue; + } + } + else + { /* S8 */ + if (samp < MIX_UNCLIP_S8_MIN) + { + origin = MIX_UNCLIP_S8_MIN; + range_end = -SINT8_MIN; + threshold = -MIX_UNCLIP_S8_MIN; + } + else if (samp > MIX_UNCLIP_S8_MAX) + { + origin = MIX_UNCLIP_S8_MAX; + range_end = SINT8_MAX; + threshold = MIX_UNCLIP_S8_MAX; + } + else + { + data += step; + continue; + } + } + + chunk = data; + + /* seek to next sample not in clipping area */ + extremum = 0; + for (len = 0; data < end_data; data += step, ++len) + { + samp = *data; + if (samp < 0) + samp = -samp; + if (samp > extremum) + extremum = samp; + if (samp <= threshold) + break; + } + + if (extremum < range_end) + continue; /* nothing to do really */ + + gain_mult = (double) (range_end - threshold) + / (double) (extremum - threshold); + + /* apply unclipping filter - clipping smooth-out */ + for (data = chunk; len; data += step, --len) + { + *data = origin + (sint32) (gain_mult * (*data - origin)); + } + } +} + +/* low quality faster version */ +static void +mixSDL_mix_lowq (uint8 *stream, sint32 len) { uint8 *end_stream = stream + len; @@ -1928,7 +2088,4 @@ void mixSDL_mix_channels (void *userdata, uint8 *stream, sint32 len) mixSDL_UnlockMutex (act_mutex); mixSDL_UnlockMutex (buf_mutex); mixSDL_UnlockMutex (src_mutex); - - (void)userdata; // satisfying compiler } - diff --git a/sc2/src/sc2code/libs/sound/mixsdl/mixer.h b/sc2/src/sc2code/libs/sound/mixsdl/mixer.h index 37ff9f294..7d5686077 100644 --- a/sc2/src/sc2code/libs/sound/mixsdl/mixer.h +++ b/sc2/src/sc2code/libs/sound/mixsdl/mixer.h @@ -184,7 +184,7 @@ typedef struct */ uint32 mixSDL_GetError (void); -bool mixSDL_OpenAudio (uint32 freq, uint32 format, uint32 chunksize); +bool mixSDL_OpenAudio (uint32 freq, uint32 format, uint32 samples_buf); void mixSDL_CloseAudio (void); bool mixSDL_QuerySpec (uint32 *freq, uint32 *format, uint32 *channels); @@ -226,42 +226,6 @@ void mixSDL_ConvertBuffer (uint32 srcfmt, void* srcdata, uint32 srcsize, uint32 srcfreq, uint32 dstfmt, void* dstdata, uint32 dstsize, uint32 dstfreq); -/************************************************* - * Internals - */ - -/* private conversion info types */ -typedef enum -{ - mixConvNone = 0, - mixConvStereoUp = 1, - mixConvStereoDown = 2, - mixConvSizeUp = 4, - mixConvSizeDown = 8 - -} mixSDL_ConvFlags; - -typedef struct -{ - uint32 srcfmt; - void *srcdata; - uint32 srcsize; - uint32 srcfreq; - uint32 srcbpc; /* bytes/sample for 1 chan */ - uint32 srcchans; - uint32 srcsamples; - - uint32 dstfmt; - void *dstdata; - uint32 dstsize; - uint32 dstfreq; - uint32 dstbpc; /* bytes/sample for 1 chan */ - uint32 dstchans; - uint32 dstsamples; - - mixSDL_ConvFlags flags; - -} mixSDL_Convertion; /* Make sure the prop-value type is of suitable size * it must be able to store both int and void* @@ -272,17 +236,9 @@ typedef struct #define MIX_COMPILE_TIME_ASSERT(name, x) \ typedef int mixSDL_dummy_##name [(x) * 2 - 1] -MIX_COMPILE_TIME_ASSERT(mixSDL_Object, sizeof(mixSDL_Object) >= sizeof(void*)); +MIX_COMPILE_TIME_ASSERT (mixSDL_Object, + sizeof(mixSDL_Object) >= sizeof(void*)); #undef MIX_COMPILE_TIME_ASSERT -/* Clipping boundaries */ -#define MIX_S16_MAX ((double) SINT16_MAX) -#define MIX_S16_MIN ((double) SINT16_MIN) -#define MIX_S8_MAX ((double) SINT8_MAX) -#define MIX_S8_MIN ((double) SINT8_MIN) - -/* Channel gain adjustment for clipping reduction */ -#define MIX_GAIN_ADJ (0.7f) - #endif /* MIXER_H */ diff --git a/sc2/src/sc2code/libs/sound/mixsdl/mixerint.h b/sc2/src/sc2code/libs/sound/mixsdl/mixerint.h new file mode 100644 index 000000000..67612b310 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/mixsdl/mixerint.h @@ -0,0 +1,125 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Simple mixer for use with SDL_audio + * Internals + */ + +#ifndef MIXERINT_H +#define MIXERINT_H + +#include "types.h" + +/************************************************* + * Internals + */ + +/* Conversion info types and funcs */ +typedef enum +{ + mixConvNone = 0, + mixConvStereoUp = 1, + mixConvStereoDown = 2, + mixConvSizeUp = 4, + mixConvSizeDown = 8 + +} mixSDL_ConvFlags; + +typedef struct +{ + uint32 srcfmt; + void *srcdata; + uint32 srcsize; + uint32 srcfreq; + uint32 srcbpc; /* bytes/sample for 1 chan */ + uint32 srcchans; + uint32 srcsamples; + + uint32 dstfmt; + void *dstdata; + uint32 dstsize; + uint32 dstfreq; + uint32 dstbpc; /* bytes/sample for 1 chan */ + uint32 dstchans; + uint32 dstsamples; + + mixSDL_ConvFlags flags; + +} mixSDL_Convertion; + +static void mixSDL_ConvertBuffer_internal (mixSDL_Convertion *conv); +static void mixSDL_Resample_frac (mixSDL_Convertion *conv); +static void mixSDL_ResampleFlat (mixSDL_Convertion *conv); +static void mixSDL_ResampleUp_fast (mixSDL_Convertion *conv); +static void mixSDL_ResampleDown_fast (mixSDL_Convertion *conv); + +static __inline__ sint32 mixSDL_GetSampleExt (void *src, uint32 bpc); +static __inline__ sint32 mixSDL_GetSampleInt (void *src, uint32 bpc); +static __inline__ void mixSDL_PutSampleInt (void *dst, uint32 bpc, + sint32 samp); +static __inline__ void mixSDL_PutSampleExt (void *dst, uint32 bpc, + sint32 samp); + +/* Source manipulation */ +static void mixSDL_SourceUnqueueAll (mixSDL_Source *src); +static void mixSDL_SourceStop_internal (mixSDL_Source *src); +static void mixSDL_SourceActivate (mixSDL_Source* src); +static void mixSDL_SourceDeactivate (mixSDL_Source* src); + + +/* Reentrant mutex */ +typedef struct +{ + Semaphore sem; + uint32 thread_id; + uint32 locks; + +} mixSDL_Mutex_info; +typedef mixSDL_Mutex_info *mixSDL_Mutex; + +static void mixSDL_InitMutex (mixSDL_Mutex mtx, char* name); +static void mixSDL_TermMutex (mixSDL_Mutex mtx); +static void mixSDL_LockMutex (mixSDL_Mutex mtx); +static void mixSDL_UnlockMutex (mixSDL_Mutex mtx); + +/* Clipping boundaries */ +#define MIX_S16_MAX ((double) SINT16_MAX) +#define MIX_S16_MIN ((double) SINT16_MIN) +#define MIX_S8_MAX ((double) SINT8_MAX) +#define MIX_S8_MIN ((double) SINT8_MIN) + +/* Channel gain adjustment for clipping reduction */ +#define MIX_GAIN_ADJ (0.8f) + +/* Clipping filter boundaries */ +#define MIX_UNCLIP_AREA 30 /* percent */ +#define MIX_UNCLIP_S16_AREA ((sint32) SINT16_MAX * MIX_UNCLIP_AREA / 100) +#define MIX_UNCLIP_S16_MAX ((sint32) SINT16_MAX - MIX_UNCLIP_S16_AREA) +#define MIX_UNCLIP_S16_MIN ((sint32) SINT16_MIN + MIX_UNCLIP_S16_AREA) +#define MIX_UNCLIP_S8_AREA ((sint32) SINT8_MAX * MIX_UNCLIP_AREA / 100) +#define MIX_UNCLIP_S8_MAX ((sint32) SINT8_MAX - MIX_UNCLIP_S8_AREA) +#define MIX_UNCLIP_S8_MIN ((sint32) SINT8_MIN + MIX_UNCLIP_S8_AREA) + +/* The Mixer */ +static void mixSDL_mix_channels (void *userdata, uint8 *stream, + sint32 len); +static void mixSDL_UnclipWorkBuffer (sint32 *data, sint32 *end_data, + uint32 step); +static void mixSDL_mix_lowq (uint8 *stream, sint32 len); +static __inline__ bool mixSDL_SourceGetNextSample (mixSDL_Source *src, + sint32* samp); + +#endif /* MIXER_H */