Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using LyraT to sample input, IIR functions to process, then output. The dsps_biquad_gen_xxx_f32 function creates garbage coefficients, I can't validate dsps_biquad_f32 as working. (DSP-111) #73

Open
mrSilkie opened this issue Jul 22, 2023 · 3 comments

Comments

@mrSilkie
Copy link

mrSilkie commented Jul 22, 2023

I'm working on this project for my Uni DSP course. I have the option of doing it in matlab or python, but we all know ESP32 + audio input / output is far more fun!

I am finding that the code is pretty undocumented. There should be different dsps_biquad_f32 functions for different types of filters but I will assume that it is correct for the coefficients generated (they are a function pair after all). Since I am working with a DAC and ADC, I have to convert int to float before processing, this is another consideration. Another consideration is that the ADC/DAC buffers are populated using [L,R,L,R,L,R] so i have my IIR function that takes the odd values of the ADC buffer, and creates a half length array that is then processed before converting the float array back into ints in the odd n positions of the DAC buffer. It operated again for the opposing channel. Since I have made some modifications and this is a 'practical application' of this project, I think there is a lot of value in this post for future readers.

The function that performs the IIR maths on the float array is

enum processChannelMode {LEFT = 0, RIGHT = 1, MONO = 2};
esp_err_t dsps_biquad_f32_stereo(const float *input, float *output, int len, float *coef, float *w)
{
    for (int i = 0; i < len; i ++)
    {
        float d0 = input[i] - coef[3]*w[0] - coef[4]*w[1]; // input[i] - a[1]*w[0] - a[2]*w[1];
        output[i] = coef[0]*d0 +  coef[1]*w[0] + coef[2]*w[1];
        w[1] = w[0];
        w[0] = d0;
    }
    return ESP_OK;
}

I modified show_iir into the following that is now stereo compatible.

// Function shows result of IIR filter
void IIRfilter(int16_t* input, int16_t* output, float freq, float qFactor, bool show, uint8_t channelMode)
{
    esp_err_t ret = ESP_OK;
    static float last_freq;
    static float coeffs_lpf[5];
    float w_lpf[5] = {0,0};

	float input_freq;
	memcpy(&input_freq, &freq, sizeof(float));

    float FloatDspBuf_IN[I2S_READLEN];
    float FloatDspBuf_OUT[I2S_READLEN];

	// INT TO FLOAT
	int k = 0;
	switch (channelMode)
	{
		case LEFT:
		for (int i = 0; i < I2S_READLEN; i += 2)
		{
			FloatDspBuf_IN[k] = ((float)input[i]) / 32768.0f;
			k++;
		}
		break;
		// Other cases not shown
	}
	
	// My code allows the changing of frequency via button press. Only process new coefficients when freq changes.
	// PRE DSP
	if (channelMode != LEFT && input_freq != last_freq)
	{
		printf("[filter-dsp] change f %2.5f: %2.5f\r\n", last_freq, input_freq);
		last_freq = input_freq;
		// Calculate iir filter coefficients
		ret = dsps_biquad_gen_lpf_f32(coeffs_lpf, input_freq, qFactor);
		// printf all of the coefficients
		for (int i = 0; i < 5; i++)
		{
			printf("[filter-dsp] Coefficients %i: %1.5f\r\n", i, coeffs_lpf[i]);
		}
		if (ret  != ESP_OK)
		{
			ESP_LOGE(TAG, "Operation error = %i", ret);
			return;
		}
	}

	// DSP
    // Process input signal
    unsigned int start_b = dsp_get_cpu_cycle_count();
	switch (channelMode)
	{
	case LEFT:
		ret = dsps_biquad_f32_stereo(FloatDspBuf_IN, FloatDspBuf_OUT, I2S_READLEN_HALF, coeffs_lpf, w_lpf);
		break;
	case RIGHT:
		ret = dsps_biquad_f32_stereo(FloatDspBuf_IN, FloatDspBuf_OUT, I2S_READLEN_HALF, coeffs_lpf, w_lpf);
		break;
	default:
		break;
	}
    unsigned int end_b = dsp_get_cpu_cycle_count();
	//printf("[filter-dsp] cpu_cycle_count : %d\r\n", end_b - start_b);
    if (ret  != ESP_OK)
    {
        ESP_LOGE(TAG, "Operation error = %i", ret);
        return;
    }

	// POST DSP

	// FLOAT TO INT
	int l = 0;
	switch (channelMode)
	{
		case LEFT:
		for (int j = 0; j < I2S_READLEN_HALF; j++)
		{
			// Clip values to int16 range
			if (FloatDspBuf_OUT[j] > 32767.0f) {
				FloatDspBuf_OUT[j] = 32767.0f;
			}
			else if (FloatDspBuf_OUT[j] < -32768.0f) {
				FloatDspBuf_OUT[j] = -32768.0f;
			}

			// Cast back to int16 and store in output at every alternate index starting from 0
			output[2*j] = (int16_t)FloatDspBuf_OUT[j];
			l++;
		}
		break;
		// Other cases not shown
	}
}

My main loop

	while (true) {
		 
		i2s_bytes_read = I2S_READLEN;
		i2s_read(I2S_NUM, i2s_buffer_read, I2S_READLEN, &i2s_bytes_read, 100);

		memset(i2s_buffer_write, 0, sizeof(i2s_buffer_write));

		freqL = freqR = filter_freq;
		IIRfilter(i2s_buffer_read, i2s_buffer_write, freqL, 0.5, false, LEFT);
		IIRfilter(i2s_buffer_read, i2s_buffer_write, freqR, 0.5, false, RIGHT);

		i2s_write(I2S_NUM, i2s_buffer_write, i2s_bytes_read, &i2s_bytes_written, 100);

        if (button) {
            filter_freq += 0.01;
            printf("Button pressed, i is now %d\n", freq_iter);
			if(filter_freq > 1.0){
				filter_freq = 0.01;
			}
        }
	}

Looking at the code, I assume that the coefficient generator is working from values 0-1, with 1 being the cutoff frequency equal to that of the sampling frequency. If so, does that mean only values 0 to 0.5 are valid since we are sampling at twice the maximum frequency?

I have found that I have no effect regardless of LPF or HPF coefficients being desired, the audio is silent up till the 60-70s where coefficient 4 starts to increase and continues to increase. I go from silence, to crackling, to a very loud and distorted output. True for both LPF and HPF.

My next plan of attack is to figure out what coefficients to use and hard code them in just so I can validate the IRR math funtion because I am not sure if it is my stereo->mono->float and float->int process is the problem, or if there are implementation problems within my code. Has anybody else had some success and is able to provide a bit more information on how to use this library?

@github-actions github-actions bot changed the title Using LyraT to sample input, IIR functions to process, then output. The dsps_biquad_gen_xxx_f32 function creates garbage coefficients, I can't validate dsps_biquad_f32 as working. Using LyraT to sample input, IIR functions to process, then output. The dsps_biquad_gen_xxx_f32 function creates garbage coefficients, I can't validate dsps_biquad_f32 as working. (DSP-111) Jul 22, 2023
@mrSilkie
Copy link
Author

I should have spent a little more time investigating before putting this post up. Reducing the change in frequency to 0.001 i have been able to hear the filter working somewhat correctly as i go from f=0.5 and up.

It starts off with okay values (still a lot of distortion)

[filter-dsp] change f 0.69699: 0.69799
[filter-dsp] Coefficients 0: 12.48261
[filter-dsp] Coefficients 1: 24.96522
[filter-dsp] Coefficients 2: 12.48261
[filter-dsp] Coefficients 3: 12.13230
[filter-dsp] Coefficients 4: 36.79814

And as it increases, the values become greater and greater creating a decimation effect

[filter-dsp] change f 0.72899: 0.72999
[filter-dsp] Coefficients 0: 71.31686
[filter-dsp] Coefficients 1: 142.63373
[filter-dsp] Coefficients 2: 71.31686
[filter-dsp] Coefficients 3: 31.77969
[filter-dsp] Coefficients 4: 252.48778

These values continue to climb before i start seeing nan values.

@dmitry1945
Copy link
Collaborator

Hi @mrSilkie ,

Thank you for the feedback.
Yes, the frequency is from 0 up to 0.5, where 1 is a sample frequency. The values above 0.5 I this are not correct.
The LPF and HPF filters are very sensitive to the qFactor.

I've planed to release the demo application this week with LyraT board and processing audio. There will be just playing samples with volume/bass/treble.
You are right, first the samples from codec should be converted from int16 to float, and then back from float to int16.
We will implement biquad filter for the stereo samples soon (next few weeks).

Thank you,
Dmitry

@mrSilkie
Copy link
Author

I got it working, I wasn't converting the int buffer to a float and back correctly.

Now that I've got it working all I can say is that it sounds like trash. A lot of digital distortion. It's getting tricky to debug the Lyra-T as SD card and Aux in both interfere with JTAG. I'll probably have to code in a file or oscillator to atleast have some test data for verification.

I have been using my button to sweep Q's and filter frequencies. Is the Q in log 10? so values 0.001 and 100 are both valid in some sense? Anyway, here is my code so far.

void int16_to_float_stereo(int16_t* input, float* output, int size, bool mode){
    switch (mode) {
        case true:
			for (int i = 0; i < size;i ++)
			{
				output[i] = input[(2*i)+1] / (float)INT16_MAX;
			}
			break;
        default:
			for (int i = 0; i < size; i++)
			{
				output[i] = input[2*i] / (float)INT16_MAX;
			}
			break;
    }
}

void float_to_int16_stereo(float* input, int16_t* output, int size, bool mode){
    switch (mode) {
        case true:
            for (int i = 1; i < size; i++) {
                output[i] = (int16_t)(input[(2*i)+1] * INT16_MAX);
            }
            break;
        default:
            for (int i = 0; i < size; i++) {
                output[i] = (int16_t)(input[2*i] * INT16_MAX);
            }
    }
}

// Function shows result of IIR filter
void IIRfilter(int16_t* input, int16_t* output, float freq, float qFactor, bool show, uint8_t channelMode)
{
    esp_err_t ret = ESP_OK;
    static float last_freq = -1.0;
    static float coeffs_lpf[5];

	static float filter_History_L[2] = {0};
	static float filter_History_R[2] = {0};

    float FloatDspBuf_IN[I2S_READLEN] = {0};
    float FloatDspBuf_OUT[I2S_READLEN] = {0};

	// INT TO FLOAT
	switch (channelMode)
	{
		case LEFT:
			int16_to_float_stereo(input, FloatDspBuf_IN, I2S_READLEN, 0);
			break;
		case RIGHT:
			int16_to_float_stereo(input, FloatDspBuf_IN, I2S_READLEN, 1);
			break;
		default:
			// no mono right now
			memcpy(output, input, I2S_READLEN);
			return;
	}
	
	// PRE DSP
	if (channelMode != LEFT && freq != last_freq)
	{
		printf("[filter-dsp] New F:%2.5f . . . Current Q: %2.3f\r\n", freq, qFactor);
		last_freq = freq;
		// Calculate iir filter coefficients
		ret = dsps_biquad_gen_hpf_f32(coeffs_lpf, (freq), qFactor);
		// printf all of the coefficients
		printf("[filter-dsp] Coefficients %1.5f\t%1.5f\t%1.5f\t%1.5f\t%1.5f\r\n", 
		coeffs_lpf[0],coeffs_lpf[1],coeffs_lpf[2],coeffs_lpf[3],coeffs_lpf[4]);
		if (ret  != ESP_OK)
		{
			ESP_LOGE(TAG, "Operation error = %i", ret);
			return;
		}
	}

	// DSP
	switch (channelMode)
	{
	case LEFT:
		ret = dsps_biquad_f32_ansi(FloatDspBuf_IN, FloatDspBuf_OUT, I2S_READLEN_HALF, coeffs_lpf, filter_History_L);
		break;
	case RIGHT:
		ret = dsps_biquad_f32_ansi(FloatDspBuf_IN, FloatDspBuf_OUT, I2S_READLEN_HALF, coeffs_lpf, filter_History_R);
		break;
	default:
		break;
	}
    if (ret  != ESP_OK)
    {
        ESP_LOGE(TAG, "Operation error = %i", ret);
        return;
    }

	// POST DSP
	// FLOAT TO INT
	switch (channelMode)
	{
		case LEFT:
			float_to_int16_stereo(FloatDspBuf_OUT, output, I2S_READLEN_HALF, 0);
			break;
		case RIGHT:
			float_to_int16_stereo(FloatDspBuf_OUT, output, I2S_READLEN_HALF, 1);
			break;
		default:
			memcpy(output, input, I2S_READLEN);
		break;
	}

	// 0 out memory
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants