diff options
Diffstat (limited to 'Alc')
-rw-r--r-- | Alc/converter.c | 332 | ||||
-rw-r--r-- | Alc/converter.h | 42 | ||||
-rw-r--r-- | Alc/mixer.c | 11 |
3 files changed, 375 insertions, 10 deletions
diff --git a/Alc/converter.c b/Alc/converter.c new file mode 100644 index 00000000..68752693 --- /dev/null +++ b/Alc/converter.c @@ -0,0 +1,332 @@ + +#include "config.h" + +#include "converter.h" + +#include "mixer_defs.h" + + +SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate) +{ + SampleConverter *converter; + + if(numchans <= 0 || srcRate <= 0 || dstRate <= 0) + return NULL; + + converter = al_calloc(16, offsetof(SampleConverter, Chan[numchans])); + converter->mSrcType = srcType; + converter->mDstType = dstType; + converter->mNumChannels = numchans; + converter->mSrcTypeSize = BytesFromDevFmt(srcType); + converter->mDstTypeSize = BytesFromDevFmt(dstType); + + converter->mSrcPrepCount = 0; + + converter->mFracOffset = 0; + converter->mIncrement = (ALsizei)clampu64((ALuint64)srcRate*FRACTIONONE/dstRate, + 1, MAX_PITCH*FRACTIONONE); + if(converter->mIncrement == FRACTIONONE) + converter->mResample = Resample_copy32_C; + else + { + /* TODO: Allow other resamplers. */ + converter->mResample = SelectResampler(LinearResampler); + } + + return converter; +} + +void DestroySampleConverter(SampleConverter **converter) +{ + if(converter) + { + al_free(*converter); + *converter = NULL; + } +} + + +static inline ALfloat Sample_ALbyte(ALbyte val) +{ return val * (1.0f/128.0f); } +static inline ALfloat Sample_ALubyte(ALubyte val) +{ return Sample_ALbyte((ALint)val - 128); } + +static inline ALfloat Sample_ALshort(ALshort val) +{ return val * (1.0f/32768.0f); } +static inline ALfloat Sample_ALushort(ALushort val) +{ return Sample_ALshort((ALint)val - 32768); } + +static inline ALfloat Sample_ALint(ALint val) +{ return (val>>7) * (1.0f/16777216.0f); } +static inline ALfloat Sample_ALuint(ALuint val) +{ return ((ALint)(val>>7) - 16777216) * (1.0f/16777216.0f); } + +static inline ALfloat Sample_ALfloat(ALfloat val) +{ return val; } + +#define DECL_TEMPLATE(T) \ +static inline void Load_##T(ALfloat *dst, const T *src, ALint srcstep, ALsizei samples)\ +{ \ + ALsizei i; \ + for(i = 0;i < samples;i++) \ + dst[i] = Sample_##T(src[i*srcstep]); \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) + +#undef DECL_TEMPLATE + +static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum DevFmtType srctype, ALsizei samples) +{ + switch(srctype) + { + case DevFmtByte: + Load_ALbyte(dst, src, srcstep, samples); + break; + case DevFmtUByte: + Load_ALubyte(dst, src, srcstep, samples); + break; + case DevFmtShort: + Load_ALshort(dst, src, srcstep, samples); + break; + case DevFmtUShort: + Load_ALushort(dst, src, srcstep, samples); + break; + case DevFmtInt: + Load_ALint(dst, src, srcstep, samples); + break; + case DevFmtUInt: + Load_ALuint(dst, src, srcstep, samples); + break; + case DevFmtFloat: + Load_ALfloat(dst, src, srcstep, samples); + break; + } +} + + +static inline ALbyte ALbyte_Sample(ALfloat val) +{ return (ALbyte)clampf(val*128.0f, -128.0f, 127.0f); } +static inline ALubyte ALubyte_Sample(ALfloat val) +{ return ALbyte_Sample(val)+128; } + +static inline ALshort ALshort_Sample(ALfloat val) +{ return (ALshort)clampf(val*32768.0f, -32768.0f, 32767.0f); } +static inline ALushort ALushort_Sample(ALfloat val) +{ return ALshort_Sample(val)+32768; } + +static inline ALint ALint_Sample(ALfloat val) +{ return (ALint)clampf(val*16777216.0f, -16777216.0f, 16777215.0f) << 7; } +static inline ALuint ALuint_Sample(ALfloat val) +{ return ALint_Sample(val)+INT_MAX+1; } + +static inline ALfloat ALfloat_Sample(ALfloat val) +{ return val; } + +#define DECL_TEMPLATE(T) \ +static inline void Store_##T(T *dst, const ALfloat *src, ALint dststep, ALsizei samples)\ +{ \ + ALsizei i; \ + for(i = 0;i < samples;i++) \ + dst[i*dststep] = T##_Sample(src[i]); \ +} + +DECL_TEMPLATE(ALbyte) +DECL_TEMPLATE(ALubyte) +DECL_TEMPLATE(ALshort) +DECL_TEMPLATE(ALushort) +DECL_TEMPLATE(ALint) +DECL_TEMPLATE(ALuint) +DECL_TEMPLATE(ALfloat) + +#undef DECL_TEMPLATE + +static void StoreSamples(ALvoid *dst, const ALfloat *src, ALint dststep, enum DevFmtType dsttype, ALsizei samples) +{ + switch(dsttype) + { + case DevFmtByte: + Store_ALbyte(dst, src, dststep, samples); + break; + case DevFmtUByte: + Store_ALubyte(dst, src, dststep, samples); + break; + case DevFmtShort: + Store_ALshort(dst, src, dststep, samples); + break; + case DevFmtUShort: + Store_ALushort(dst, src, dststep, samples); + break; + case DevFmtInt: + Store_ALint(dst, src, dststep, samples); + break; + case DevFmtUInt: + Store_ALuint(dst, src, dststep, samples); + break; + case DevFmtFloat: + Store_ALfloat(dst, src, dststep, samples); + break; + } +} + + +ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes) +{ + ALint prepcount = converter->mSrcPrepCount; + ALsizei increment = converter->mIncrement; + ALsizei DataPosFrac = converter->mFracOffset; + ALuint64 DataSize64; + + if(prepcount < 0) + { + /* Negative prepcount means we need to skip that many input samples. */ + if(-prepcount >= srcframes) + return 0; + srcframes += prepcount; + prepcount = 0; + } + + if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES && + MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= srcframes) + { + /* Not enough input samples to generate an output sample. */ + return 0; + } + + DataSize64 = prepcount; + DataSize64 += srcframes; + DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES; + DataSize64 <<= FRACTIONBITS; + DataSize64 -= DataPosFrac; + + /* If we have a full prep, we can generate at least one sample. */ + return (ALsizei)clampu64(DataSize64/increment, 1, INT_MAX); +} + + +ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid *src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes) +{ + const ALsizei SrcFrameSize = converter->mNumChannels * converter->mSrcTypeSize; + const ALsizei DstFrameSize = converter->mNumChannels * converter->mDstTypeSize; + const ALsizei increment = converter->mIncrement; + ALsizei pos = 0; + + while(pos < dstframes) + { + ALfloat *restrict SrcData = ASSUME_ALIGNED(converter->mSrcSamples, 16); + ALfloat *restrict DstData = ASSUME_ALIGNED(converter->mDstSamples, 16); + ALint prepcount = converter->mSrcPrepCount; + ALsizei DataPosFrac = converter->mFracOffset; + ALuint64 DataSize64; + ALsizei DstSize; + ALint toread; + ALsizei chan; + + if(prepcount < 0) + { + /* Negative prepcount means we need to skip that many input samples. */ + if(-prepcount >= *srcframes) + { + converter->mSrcPrepCount = prepcount + *srcframes; + *srcframes = 0; + break; + } + src = (ALbyte*)src + SrcFrameSize*-prepcount; + *srcframes += prepcount; + prepcount = 0; + } + toread = mini(*srcframes, BUFFERSIZE-(MAX_POST_SAMPLES+MAX_PRE_SAMPLES)); + + if(prepcount < MAX_POST_SAMPLES+MAX_PRE_SAMPLES && + MAX_POST_SAMPLES+MAX_PRE_SAMPLES-prepcount >= toread) + { + /* Not enough input samples to generate an output sample. Store + * what we're given for later. + */ + for(chan = 0;chan < converter->mNumChannels;chan++) + LoadSamples(&converter->Chan[chan].mPrevSamples[prepcount], + (const ALbyte*)src + converter->mSrcTypeSize*chan, + converter->mNumChannels, converter->mSrcType, toread + ); + + converter->mSrcPrepCount = prepcount + toread; + *srcframes = 0; + break; + } + + DataSize64 = prepcount; + DataSize64 += toread; + DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES; + DataSize64 <<= FRACTIONBITS; + DataSize64 -= DataPosFrac; + + /* If we have a full prep, we can generate at least one sample. */ + DstSize = (ALsizei)clampu64(DataSize64/increment, 1, BUFFERSIZE); + DstSize = mini(DstSize, dstframes-pos); + + for(chan = 0;chan < converter->mNumChannels;chan++) + { + const ALbyte *SrcSamples = (const ALbyte*)src + converter->mSrcTypeSize*chan; + ALbyte *DstSamples = (ALbyte*)dst + converter->mSrcTypeSize*chan; + const ALfloat *ResampledData; + ALsizei SrcDataEnd; + + /* Load the previous samples into the source data first, then the + * new samples from the input buffer. + */ + memcpy(SrcData, converter->Chan[chan].mPrevSamples, + prepcount*sizeof(ALfloat)); + LoadSamples(SrcData + prepcount, SrcSamples, + converter->mNumChannels, converter->mSrcType, toread + ); + + /* Store as many prep samples for next time as possible, given the + * number of output samples being generated. + */ + SrcDataEnd = (DataPosFrac + increment*DstSize)>>FRACTIONBITS; + if(SrcDataEnd >= prepcount+toread) + memset(converter->Chan[chan].mPrevSamples, 0, + sizeof(converter->Chan[chan].mPrevSamples)); + else + { + size_t len = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES, prepcount+toread-SrcDataEnd); + memcpy(converter->Chan[chan].mPrevSamples, &SrcData[SrcDataEnd], + len*sizeof(ALfloat)); + memset(converter->Chan[chan].mPrevSamples+len, 0, + sizeof(converter->Chan[chan].mPrevSamples) - len*sizeof(ALfloat)); + } + + /* Now resample, and store the result in the output buffer. */ + ResampledData = converter->mResample(NULL, + SrcData+MAX_PRE_SAMPLES, DataPosFrac, increment, + DstData, DstSize + ); + + StoreSamples(DstSamples, ResampledData, converter->mNumChannels, + converter->mDstType, DstSize); + } + + /* Update the number of prep samples still available, as well as the + * fractional offset. + */ + DataPosFrac += increment*DstSize; + converter->mSrcPrepCount = mini(MAX_PRE_SAMPLES+MAX_POST_SAMPLES, + prepcount+toread-(DataPosFrac>>FRACTIONBITS)); + converter->mFracOffset = DataPosFrac & FRACTIONMASK; + + /* Update the src and dst pointers in case there's still more to do. */ + src = (const ALbyte*)src + SrcFrameSize*(DataPosFrac>>FRACTIONBITS); + *srcframes -= mini(*srcframes, (DataPosFrac>>FRACTIONBITS)); + + dst = (ALbyte*)dst + DstFrameSize*DstSize; + pos += DstSize; + } + + return pos; +} diff --git a/Alc/converter.h b/Alc/converter.h new file mode 100644 index 00000000..76b2520f --- /dev/null +++ b/Alc/converter.h @@ -0,0 +1,42 @@ +#ifndef CONVERTER_H +#define CONVERTER_H + +#include "alMain.h" +#include "alu.h" + +#ifdef __cpluspluc +extern "C" { +#endif + +typedef struct SampleConverter { + enum DevFmtType mSrcType; + enum DevFmtType mDstType; + ALsizei mNumChannels; + ALsizei mSrcTypeSize; + ALsizei mDstTypeSize; + + ALint mSrcPrepCount; + + ALsizei mFracOffset; + ALsizei mIncrement; + ResamplerFunc mResample; + + alignas(16) ALfloat mSrcSamples[BUFFERSIZE+MAX_PRE_SAMPLES+MAX_POST_SAMPLES]; + alignas(16) ALfloat mDstSamples[BUFFERSIZE]; + + struct { + alignas(16) ALfloat mPrevSamples[MAX_PRE_SAMPLES+MAX_POST_SAMPLES]; + } Chan[]; +} SampleConverter; + +SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate); +void DestroySampleConverter(SampleConverter **converter); + +ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid *src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes); +ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes); + +#ifdef __cpluspluc +} +#endif + +#endif /* CONVERTER_H */ diff --git a/Alc/mixer.c b/Alc/mixer.c index 2a30e323..90f3e05e 100644 --- a/Alc/mixer.c +++ b/Alc/mixer.c @@ -44,15 +44,6 @@ static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE, extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size); -enum Resampler { - PointResampler, - LinearResampler, - FIR4Resampler, - BSincResampler, - - ResamplerDefault = LinearResampler -}; - /* BSinc requires up to 11 extra samples before the current position, and 12 after. */ static_assert(MAX_PRE_SAMPLES >= 11, "MAX_PRE_SAMPLES must be at least 11!"); static_assert(MAX_POST_SAMPLES >= 12, "MAX_POST_SAMPLES must be at least 12!"); @@ -103,7 +94,7 @@ static inline HrtfMixerFunc SelectHrtfMixer(void) return MixHrtf_C; } -static inline ResamplerFunc SelectResampler(enum Resampler resampler) +ResamplerFunc SelectResampler(enum Resampler resampler) { switch(resampler) { |