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
This commit is contained in:
avolkov
2003-02-05 03:43:51 +00:00
parent 23f8be1d0c
commit acfdfa9eda
3 changed files with 345 additions and 107 deletions
+217 -60
View File
@@ -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
}
+3 -47
View File
@@ -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 */
@@ -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 */