providing the actual anti-aliasing attenuation and resampling filter lengthdds
| @@ -129,6 +129,12 @@ void Resampler::setFilter(int32_t halfFiltLength,int32_t overSampling, float cut | |||
| double Resampler::getStep() const { | |||
| return _stepAdapted; | |||
| } | |||
| double Resampler::getAttenuation() const { | |||
| return _attenuation; | |||
| } | |||
| int32_t Resampler::getHalfFilterLength() const{ | |||
| return _halfFilterLength; | |||
| } | |||
| void Resampler::reset(){ | |||
| _initialized=false; | |||
| } | |||
| @@ -136,6 +142,7 @@ void Resampler::configure(float fs, float newFs, float attenuation, int32_t minH | |||
| // Serial.print("configure, fs: "); | |||
| // Serial.println(fs); | |||
| if (fs<=0. || newFs <=0.){ | |||
| _attenuation=0; | |||
| _initialized=false; | |||
| return; | |||
| } | |||
| @@ -152,9 +159,10 @@ void Resampler::configure(float fs, float newFs, float attenuation, int32_t minH | |||
| float cutOffFrequ, kaiserBeta; | |||
| _overSamplingFactor=1024; | |||
| if (fs <= newFs){ | |||
| _attenuation=0; | |||
| cutOffFrequ=1.; | |||
| kaiserBeta=10; | |||
| _halfFilterLength=minHalfFilterLength; | |||
| _halfFilterLength=min(minHalfFilterLength,MAX_HALF_FILTER_LENGTH); | |||
| } | |||
| else{ | |||
| cutOffFrequ=newFs/fs; | |||
| @@ -204,6 +212,7 @@ void Resampler::configure(float fs, float newFs, float attenuation, int32_t minH | |||
| int32_t f = (noSamples-1)/(MAX_FILTER_SAMPLES-1)+1; | |||
| _overSamplingFactor/=f; | |||
| } | |||
| _attenuation=attenuation; | |||
| } | |||
| #ifdef DEBUG_RESAMPLER | |||
| @@ -69,6 +69,8 @@ class Resampler { | |||
| void addToPos(double val); | |||
| void fixStep(); | |||
| bool initialized() const; | |||
| double getAttenuation() const; | |||
| int32_t getHalfFilterLength() const; | |||
| //resampling NOCHANNELS channels. Performance is increased a lot if the number of channels is known at compile time -> the number of channels is a template argument | |||
| template <uint8_t NOCHANNELS> | |||
| @@ -206,7 +208,7 @@ class Resampler { | |||
| float* _endOfBuffer[MAX_NO_CHANNELS]; | |||
| int32_t _overSamplingFactor; | |||
| int32_t _halfFilterLength; | |||
| int32_t _halfFilterLength=0; | |||
| int32_t _filterLength; | |||
| bool _initialized=false; | |||
| @@ -218,6 +220,8 @@ class Resampler { | |||
| double _cPos; | |||
| double _sum; | |||
| double _oldDiffs[2]; | |||
| double _attenuation=0; | |||
| }; | |||
| #endif | |||
| @@ -36,6 +36,7 @@ namespace { | |||
| #define SPDIF_RX_BUFFER_LENGTH AUDIO_BLOCK_SAMPLES | |||
| const int32_t bufferLength=8*AUDIO_BLOCK_SAMPLES; | |||
| const uint16_t noSamplerPerIsr=SPDIF_RX_BUFFER_LENGTH/4; | |||
| const float toFloatAudio= 1.f/pow(2., 23.); | |||
| } | |||
| volatile bool AsyncAudioInputSPDIF3::resetResampler=true; | |||
| @@ -199,14 +200,13 @@ void AsyncAudioInputSPDIF3::isr(void) | |||
| #endif | |||
| float *destR = &(bufferR[buffer_offset]); | |||
| float *destL = &(bufferL[buffer_offset]); | |||
| const float factor= pow(2., 23.)+1; | |||
| do { | |||
| int32_t n=(*src) & 0x800000 ? (*src)|0xFF800000 : (*src) & 0xFFFFFF; | |||
| *destL++ = (float)(n)/factor; | |||
| *destL++ = (float)(n)*toFloatAudio; | |||
| ++src; | |||
| n=(*src) & 0x800000 ? (*src)|0xFF800000 : (*src) & 0xFFFFFF; | |||
| *destR++ = (float)(n)/factor; | |||
| *destR++ = (float)(n)*toFloatAudio; | |||
| ++src; | |||
| } while (src < end); | |||
| buffer_offset=(buffer_offset+SPDIF_RX_BUFFER_LENGTH/4)%bufferLength; | |||
| @@ -280,8 +280,9 @@ void AsyncAudioInputSPDIF3::configure(){ | |||
| if (abs(frequDiff) > 0.01 || !_resampler.initialized()){ | |||
| //the new sample frequency differs from the last one -> configure the _resampler again | |||
| _inputFrequency=inputF; | |||
| const int32_t targetLatency=round(_targetLatencyS*inputF); | |||
| _targetLatencyS=max(0.001,(noSamplerPerIsr*3./2./_inputFrequency)); | |||
| _maxLatency=max(2.*_blockDuration, 2*noSamplerPerIsr/_inputFrequency); | |||
| const int32_t targetLatency=round(_targetLatencyS*inputF); | |||
| __disable_irq(); | |||
| resample_offset = targetLatency <= buffer_offset ? buffer_offset - targetLatency : bufferLength -(targetLatency-buffer_offset); | |||
| __enable_irq(); | |||
| @@ -402,8 +403,9 @@ void AsyncAudioInputSPDIF3::update(void) | |||
| double AsyncAudioInputSPDIF3::getInputFrequency() const{ | |||
| __disable_irq(); | |||
| double f=_lastValidInputFrequ; | |||
| bool l=locked; | |||
| __enable_irq(); | |||
| return f; | |||
| return l ? f : 0.; | |||
| } | |||
| double AsyncAudioInputSPDIF3::getTargetLantency() const { | |||
| __disable_irq(); | |||
| @@ -411,6 +413,12 @@ double AsyncAudioInputSPDIF3::getTargetLantency() const { | |||
| __enable_irq(); | |||
| return l ; | |||
| } | |||
| double AsyncAudioInputSPDIF3::getAttenuation() const{ | |||
| return _resampler.getAttenuation(); | |||
| } | |||
| int32_t AsyncAudioInputSPDIF3::getHalfFilterLength() const{ | |||
| return _resampler.getHalfFilterLength(); | |||
| } | |||
| void AsyncAudioInputSPDIF3::config_spdifIn(){ | |||
| //CCM Clock Gating Register 5, imxrt1060_rev1.pdf page 1145 | |||
| CCM_CCGR5 |=CCM_CCGR5_SPDIF(CCM_CCGR_ON); //turn spdif clock on - necessary for receiver! | |||
| @@ -51,6 +51,8 @@ public: | |||
| double getInputFrequency() const; | |||
| bool isLocked() const; | |||
| double getTargetLantency() const; | |||
| double getAttenuation() const; | |||
| int32_t getHalfFilterLength() const; | |||
| protected: | |||
| static DMAChannel dma; | |||
| static void isr(void); | |||
| @@ -84,10 +86,10 @@ private: | |||
| volatile double _bufferedTime; | |||
| volatile double _lastValidInputFrequ; | |||
| double _inputFrequency; | |||
| double _inputFrequency=0.; | |||
| double _targetLatencyS; //target latency [seconds] | |||
| const double _blockDuration=AUDIO_BLOCK_SAMPLES/AUDIO_SAMPLE_RATE; //[seconds] | |||
| const double _maxLatency=2.*_blockDuration; | |||
| const double _blockDuration=AUDIO_BLOCK_SAMPLES/AUDIO_SAMPLE_RATE_EXACT; //[seconds] | |||
| double _maxLatency=2.*_blockDuration; | |||
| #ifdef DEBUG_SPDIF_IN | |||
| static volatile bool bufferOverflow; | |||