applying esp-dsp IIR biquad to pipeline or element ?

benbiles
Posts: 32
Joined: Sun Dec 15, 2019 4:46 pm

applying esp-dsp IIR biquad to pipeline or element ?

Postby benbiles » Mon Dec 16, 2019 3:22 am

Hi,

I'd like to modify an I2s audio stream with something like

int NbufferSize = 1024;
float coeffs[5]; // the 5 IIR bi quad coefficients in this array
int delay(0,0);

dsps_biquad_f32 (&NsamplesBufferInPointer, &iNsamplesBufferOutPointer, NbufferSize, coeffs, delay);

Do I create my own element and that to the elements chain ( pipeline ) ?
Which callback to use for timing ? how do I know when the buffer is full and when to modify samples in buffer ?

I have successfully merged equalizer example with passthrough.
Would equalizer be the best starting point add the dsps_biquad_f32 coefficient?

I'm new to the esp-adf ( esp-idf ) environment.

User avatar
shabtronic
Posts: 49
Joined: Sun Nov 03, 2019 1:33 pm

Re: applying esp-dsp IIR biquad to pipeline or element ?

Postby shabtronic » Mon Dec 16, 2019 6:35 pm

Hey BenBiles


I'm working on a similar part of my code for my ESP32 - I posted a few messages ago how to add a DSP function into the pipeline - yes it's based off the eq code example.

https://esp32.com/viewtopic.php?f=20&t=13495

you'd pop ur biquad code in here somewhere:

Code: Select all

static audio_element_err_t Dsp_process(audio_element_handle_t self, char *inbuf, int len)
	{
	audio_element_input(self, (char *)DspBuf, len);
	//Biquad code here!
	int ret = audio_element_output(self, (char *)DspBuf, len);
	return (audio_element_err_t)ret;
	}
Hope that helps, and hope I'm correct :)

benbiles
Posts: 32
Joined: Sun Dec 15, 2019 4:46 pm

Re: applying esp-dsp IIR biquad to pipeline or element ?

Postby benbiles » Tue Dec 17, 2019 5:53 am

Hi shabtronic!

Thanks for this ! You saved me hours of head scratching and have done exactly what I thought I needed to do!

Did you just add your additional dsp functions etc to the equalizer.c or did you make a new file dspProcess.c and a dspProcess.h header?

I'll try and insert my code to your Dsp_process function and report back here if it works soon as I get time :)

User avatar
shabtronic
Posts: 49
Joined: Sun Nov 03, 2019 1:33 pm

Re: applying esp-dsp IIR biquad to pipeline or element ?

Postby shabtronic » Tue Dec 17, 2019 4:30 pm

Umm I just looked at the equalizer src code and copied what it did - it's for my own FX reverb,distortion - so it's all currently in my main.cpp until I'm happy with my code - it's mostly R&D dsp stuff at the moment. But that code does work - I followed the code path and realised:

audio_element_input(self, (char *)DspBuf, len);

and

audio_element_output(self, (char *)DspBuf, len);

It's a pretty smart setup - those two functions pull and push data in the audiopipeline from the previous and next links. Worth spending some time seeing what and how they do that (either from a ringbuffer or a read/write callback).


Have fun!

benbiles
Posts: 32
Joined: Sun Dec 15, 2019 4:46 pm

Re: applying esp-dsp IIR biquad to pipeline or element ?

Postby benbiles » Wed Dec 18, 2019 9:06 am

It looks like I got your hacked equalizer.c code working but maybe in a lightly different way.

audio_element_handle_t Dsp_init() // no equalizer array to pass in !
{
audio_element_cfg_t DspCfg; // = DEFAULT_AUDIO_ELEMENT_CONFIG();
memset(&DspCfg, 0, sizeof(audio_element_cfg_t));
DspCfg.destroy = Dsp_destroy;
DspCfg.process = Dsp_process;
DspCfg.read = Dsp_read; // why are these needed if they are not called?
DspCfg.write = Dsp_write; // why are these needed if they are not called?
DspCfg.open = Dsp_open;
DspCfg.close = Dsp_close;
DspCfg.buffer_len = (1024);
DspCfg.tag = "Dsp";
DspCfg.task_stack = (2 * 1024);
DspCfg.task_prio = (5);
DspCfg.task_core = (0); // core 1 has FPU issues lol
DspCfg.out_rb_size = (8 * 1024);

audio_element_handle_t DspProcessor = audio_element_init(&DspCfg);
return DspProcessor;
}


in my main based on passthrough example i have..

audio_element_handle_t i2s_stream_reader,DspProcessor,i2s_stream_writer; // equalizer disabled

DspProcessor = Dsp_init();
audio_pipeline_register(pipeline, DspProcessor, "DspProcessor");
audio_pipeline_link(pipeline, (const char *[]) {"i2s_read", "DspProcessor", "i2s_write"}, 3); // N components in audio pipeline

I think the only real difference I have is.. return DspProcessor;

I changed the audio to 32bit 96khz so changed your buffer from SHORT to float DspBuf[4096];

This works fine until I add my one DSP line then nothing..

I have 5 biquad values in DSP_iir_coeffs array and {0,0} in DSP_delay.

dsps_biquad_f32_ae32(DspBuf,DspBuf,len,DSP_iir_coeffs,DSP_delay);

Do you know if lyrat in 32bit mode I2s is +1 / -1 32bit float samples ?

I actually wanted to make DSP biquad filter in signed 16bit but the ESP-DSP lib doesn't seam to do signed 16bi DSP IIR biquad.

Are you using ESP-DSP ? or another DSP lib ?

I can sort of see how your functions work with the callbacks , pretty nice !
Great idea to make a guitar FX processor.

I suppose I'll just leave it as a hacked equalizier.c file as I guess they will add a DSP element into ESP-ADF soon.

User avatar
shabtronic
Posts: 49
Joined: Sun Nov 03, 2019 1:33 pm

Re: applying esp-dsp IIR biquad to pipeline or element ?

Postby shabtronic » Wed Dec 18, 2019 6:26 pm

Cool stuff!!

Umm I'm using a A1S board at the moment - switching to a lyrat board later on (mainly because the A1S board the audio amps are close to the ESP32 Chip and cause audio noise when you turn on the wifi :) )

Don't know about the Lyrat drivers - but you can look at the driver code in adf/components/audio_board_driver e.t.c. to see what it does in 32bit mode. But I'm sure it's not float - generally 24bit output is the highest end user quality (and it's great) 32bit mode would be the same as 24bit mode but with the lower bits ignored. I could be wrong - but that's generally how it used to work :)

I'm porting across my super awesome guitar VST stuff - always wanted it hardware - and the ESP boards are perfect for a terrible soldering person like myself - everything on board - no need to mess it up with terrible soldering :)

All my DSP is mostly written by myself - oversampling/integrating distortion e.t.c.
tho my EQ code comes from the RBJ cookbook and my convolution code comes from reapers WDL sdk.

I also got the ESP boards - because they have bluetooth at low level. I have a cheap muslady wireless guitar system. Seems like it's sitting on a hacked bluetooth stream - so I'm hoping I can hack up the bluetooth on the ESP to get my guitar input - I really hope that's possible!

I can post my EQ code if you want it - it's cpp and geared for PC tho - will need converting!

benbiles
Posts: 32
Joined: Sun Dec 15, 2019 4:46 pm

Re: applying esp-dsp IIR biquad to pipeline or element ?

Postby benbiles » Thu Dec 19, 2019 7:33 am

Thanks for the offer of the EQ code , not sure I have the coding ability to convert PC code to ESP32 !
might come back here asking for it if I fail to figure out how to just use the IDF-DSP lib.

your right , the codec 'ES8388' on lyrat 4.3 is max 24-bit 96 kHz sampling frequency ( in fact 96khz is'nt in the HAL default config so maybe only 48k support )

I switched the I2s pipline and HAL back to 48khz and 16bit and the samples are getting through the DSP function now.
They don't sound modified correctly and there a bit scratchy !!

there is a DSP lib option in the make menuconfig now , maybe I enabled that by running make menuconfig so now it works.

At a guess I would need to convert the samples to and from float either side of the DSP IIR function to get it to process the numbers
properly and I'm not sure the ESP32 can do that fast enough for a realtime pipeline or not.

I'll give it a go next and let you know here if I get it to work.

generally noise isn't bad on the lyrat 4.3 although I haven't had time to even connect the wifi to my router yet !
only just got the board. separate power LDO's on the board seam to help with noise taking a look at the board layout.

ES8388 is not the best codec I've ever used but it will do for basic dev :)

benbiles
Posts: 32
Joined: Sun Dec 15, 2019 4:46 pm

Re: applying esp-dsp IIR biquad to pipeline or element ?

Postby benbiles » Thu Dec 19, 2019 3:36 pm

working :D :D :D

int16 samples --> float --> DSP --> float --> int16 samples

I'll check if I can use the ESP-DSP lib maths functions to see if I can translate the int16 samples to and from float.

The main problem apart from converting the samples to float and back was my IIR coefficients i'd generated elsewhere were
for 96khz samples and I'm not sure they were compatible with the ADF-DSP library.

works now by generating the coeffs in the code below taken from ADF-DSP IIR example

thanks again for your help !



// should match bit rate in pipeline?
int16_t DspBuf[4096];

float coeffs_lpf[5];
float w_lpf[5] = {0,0};
#define NNN 4096
float FloatDspBuf[NNN];
float FloatDspBufB[NNN];
const float *pFloatDspBuf = FloatDspBuf;


// ********** PROCESS the buffer with DSP IIR !!

static audio_element_err_t Dsp_process(audio_element_handle_t self, char *inbuf, int len)
{


audio_element_input(self, (char *)DspBuf, len);

// ************* DSP Process DspBuf here ********************************



// Calculate iir filter coefficients ( instead of preset )

// generate low pass filter

float freq = 4000;
float qFactor = 200;

esp_err_t rety = ESP_OK;

rety = dsps_biquad_gen_lpf_f32(coeffs_lpf, freq, qFactor); // low pass
// rety = dsps_biquad_gen_lpf_f32(coeffs_hpf, freq, qFactor); // high pass

if (rety != ESP_OK) { ESP_LOGE(TAG, "Operation error dsps_biquad_gen_lpf_f32 = %i", rety);
return rety; }


// convert 16bit audio smaples to float ****************
for ( int i = 0; i < len; i++ )
{
// do this properly with ESP-DSP maths ?
FloatDspBuf = ((float)DspBuf) / (float)32768;
}

// DSP IIR biquad process
esp_err_t rett = ESP_OK;
rett = dsps_biquad_f32(pFloatDspBuf,FloatDspBufB,len,coeffs_lpf,w_lpf);

if (rett != ESP_OK) { ESP_LOGE(TAG, "Operation error = %i", rett);
return rett; }

// convert float audio samples back into 16bit audio samples for pipeline
for ( int j = 0; j < len; j++ )
{
// do this properly with ESP-DSP maths ?
FloatDspBufB[j] = FloatDspBufB[j] * 32768 ;
if( FloatDspBufB[j] > 32767 ) FloatDspBufB[j] = 32767;
if( FloatDspBufB[j] < -32768 ) FloatDspBufB[j] = -32768;
DspBuf[j] = (int16_t)FloatDspBufB[j]; // cast back
}

/// ************* END DSP Process ********************************

// DspBuf samples back into pipeline

int ret = audio_element_output(self, (char *)DspBuf, len);
return (audio_element_err_t)ret;
}

User avatar
shabtronic
Posts: 49
Joined: Sun Nov 03, 2019 1:33 pm

Re: applying esp-dsp IIR biquad to pipeline or element ?

Postby shabtronic » Thu Dec 19, 2019 6:13 pm

Cool stuff - awesome you got it working!

Wouldn't worry about float->int and int->float conversion - there's usually a fast fpu instruction for that.

I did some early tests on the FPU as a feasibility study for my projects - and it seems fine for some lightweight DSP. A Biquad EQ is super stable and super cheap:

Code: Select all

inline double process(double in)
{
double out = in * b0 + l1;
l1 = in * b1 + l2 - a1 * out;
l2 = in * b2 - a2 * out;
return out;
}
10 Band EQ - would be 20 of those for stereo setup with Peak EQ coeff's - ESP32 @ 240mhz could eat that for breakfast and still run a marathon :P

benbiles
Posts: 32
Joined: Sun Dec 15, 2019 4:46 pm

Re: applying esp-dsp IIR biquad to pipeline or element ?

Postby benbiles » Fri Dec 20, 2019 3:21 am

Interesting, so I could just do the biquad eq in C maybe.

How come your using inline double in your example rather than float? Inline double is a 64bit float?

I should take a look at how the 10 band eq in eualizer.c is actually working!! It's probebly exactly that 😂

Equalizer.c is limited to - 13db attenuation where I needed - 20db to 20db attenuation across 20hz to 20khz

Sort of prefer trying to learn how things actually work rather than relying on example code to much :)

I still have a buzzing sound in my DSP biquad and it's in mono. Should be in stereo.

I'm still not convinced I'm converting to the correct float type for esp-dsp. I'll try inline double!

The example I'm using required a delay line of 2,2

I'm assuming a esp32 float is 32bit and that esp-dsp wants 32bit float but maybe that's wrong.

Also assumed if I process all the data in the pipeline buffer I should be processing left and right data. I guess your guitar FX code is all single channel though? Unless you do stereo chorus effect ( popular in the 80's) 😂

I'll get there in the end. Maybe it's worth putting it in github when it works properly if esp don't do it before me.

Who is online

Users browsing this forum: No registered users and 19 guests