Skip to content

Commit

Permalink
Add Biquad IIR DSP function and use a namespace (#12)
Browse files Browse the repository at this point in the history
* add biquad IIR filter assembly functions

* use esp_audio_libs global namespace
  • Loading branch information
kahrendt authored Jan 16, 2025
1 parent 146077c commit ccc1197
Show file tree
Hide file tree
Showing 19 changed files with 298 additions and 68 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# esp-audio-libs

A collection of libraries and functions that are useful for playing audio on ESP32 devices. It includes code based on the following:
- [esp-dsp](https://github.com/espressif/esp-dsp) assembly functions for floating point dot products and Q15 fixed point addition and constant multipliction.
- [esp-dsp](https://github.com/espressif/esp-dsp) assembly functions for floating point dot product and biquad IIR filters and Q15 fixed point addition and constant multipliction.
- Author: Espressif
- License: Apache v2.0
- [ART-resampler](https://github.com/dbry/audio-resampler) for resampling audio, optimized with assembly dot product functions.
Expand Down
38 changes: 18 additions & 20 deletions include/art_biquad.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,34 @@
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////

// biquad.h
#pragma once

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <math.h>

typedef struct {
float a0, a1, a2, b1, b2;
} BiquadCoefficients;
namespace esp_audio_libs {
namespace art_resampler {

typedef struct {
BiquadCoefficients coeffs; // coefficients
float in_d1, in_d2; // delayed input
float out_d1, out_d2; // delayed output
int first_order; // optimization
} Biquad;
typedef struct {
float a0, a1, a2, b1, b2;
} BiquadCoefficients;

#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
BiquadCoefficients coeffs; // coefficients
float in_d1, in_d2; // delayed input
float out_d1, out_d2; // delayed output
int first_order; // optimization
} Biquad;

void biquad_init(Biquad *f, const BiquadCoefficients *coeffs, float gain);
void biquad_init(Biquad * f, const BiquadCoefficients *coeffs, float gain);

void biquad_lowpass(BiquadCoefficients *filter, double frequency);
void biquad_highpass(BiquadCoefficients *filter, double frequency);
void biquad_lowpass(BiquadCoefficients * filter, double frequency);
void biquad_highpass(BiquadCoefficients * filter, double frequency);

void biquad_apply_buffer(Biquad *f, float *buffer, int num_samples, int stride);
float biquad_apply_sample(Biquad *f, float input);
void biquad_apply_buffer(Biquad * f, float *buffer, int num_samples, int stride);
float biquad_apply_sample(Biquad * f, float input);

#ifdef __cplusplus
}
#endif
} // namespace esp_audio_libs
12 changes: 5 additions & 7 deletions include/art_resampler.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#include <math.h>
#include <stdbool.h>

namespace esp_audio_libs {
namespace art_resampler {

#define SUBSAMPLE_INTERPOLATE 0x1
#define BLACKMAN_HARRIS 0x2
#define INCLUDE_LOWPASS 0x4
Expand All @@ -30,10 +33,6 @@ typedef struct {
unsigned int input_used, output_generated;
} ResampleResult;

#ifdef __cplusplus
extern "C" {
#endif

Resample *resampleInit(int numChannels, int numTaps, int numFilters, float lowpassRatio, int flags);
ResampleResult resampleProcess(Resample *cxt, const float *const *input, int numInputFrames, float *const *output,
int numOutputFrames, float ratio);
Expand All @@ -46,6 +45,5 @@ float resampleGetPosition(Resample *cxt);
void resampleReset(Resample *cxt);
void resampleFree(Resample *cxt);

#ifdef __cplusplus
}
#endif
} // namespace art_resampler
} // namespace esp_audio_libs
7 changes: 3 additions & 4 deletions include/flac_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
// Uses some small parts from: https://github.com/schreibfaul1/ESP32-audioI2S/
// See also: https://xiph.org/flac/format.html

#ifndef _FLAC_DECODER_H
#define _FLAC_DECODER_H
#pragma once

#include <cstddef>
#include <cstdint>
#include <vector>

namespace esp_audio_libs {
namespace flac {

// 'fLaC'
Expand Down Expand Up @@ -185,5 +185,4 @@ class FLACDecoder {
};

} // namespace flac

#endif
} // namespace esp_audio_libs
9 changes: 6 additions & 3 deletions include/mp3_decoder.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#ifndef MP3_DECODER_H_
#define MP3_DECODER_H_
#pragma once

#include <stdlib.h>
#include <string.h>
#include <xtensa/config/core-isa.h>

namespace esp_audio_libs {
namespace helix_decoder {

#define ASSERT(x) /* do nothing */

/* determining MAINBUF_SIZE:
Expand Down Expand Up @@ -379,4 +381,5 @@ void MP3GetLastFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo);
int MP3GetNextFrameInfo(HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo, unsigned char *buf);
int MP3FindSyncWord(unsigned char *buf, int nBytes);

#endif // MP3_DECODER_H_
} // namespace helix_decoder
} // namespace esp_audio_libs
8 changes: 5 additions & 3 deletions include/resampler.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <algorithm>
#include <esp_heap_caps.h>

namespace esp_audio_libs {
namespace resampler {

// Class that wraps the ART resampler for straightforward use
Expand Down Expand Up @@ -59,10 +60,10 @@ class Resampler {
float *float_output_buffer_{nullptr};
size_t output_buffer_samples_;

Resample *resampler_{nullptr};
art_resampler::Resample *resampler_{nullptr};

Biquad lowpass_[2][2];
BiquadCoefficients lowpass_coeff_;
art_resampler::Biquad lowpass_[2][2];
art_resampler::BiquadCoefficients lowpass_coeff_;

uint16_t number_of_taps_;
uint16_t number_of_filters_;
Expand All @@ -79,3 +80,4 @@ class Resampler {
uint8_t channels_;
};
} // namespace resampler
} // namespace esp_audio_libs
7 changes: 6 additions & 1 deletion include/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <cmath>
#include <stdint.h>

namespace esp_audio_libs {

/// @brief Converts an array of quantized samples with the specified number of bits into floating point samples.
/// @param input_buffer Pointer to the input quantized samples aligned to the byte
/// @param output_buffer Poitner to the output floating point samples
Expand All @@ -18,4 +20,7 @@ void quantized_to_float(const uint8_t *input_buffer, float *output_buffer, uint3
/// @param num_samples Number of samples to convert
/// @param output_bits Number of bits per sample for the quantized samples
/// @return Number of clipped samples
uint32_t float_to_quantized(const float *input_buffer, uint8_t *output_buffer, uint32_t num_samples, uint8_t output_bits);
uint32_t float_to_quantized(const float *input_buffer, uint8_t *output_buffer, uint32_t num_samples,
uint8_t output_bits);

} // namespace esp_audio_libs
7 changes: 3 additions & 4 deletions include/wav_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// data portion of the file.
// Skips over extraneous chunks like LIST and INFO.

#ifndef WAV_DECODER_H_
#define WAV_DECODER_H_
#pragma once

#include <cstdint>
#include <string>
Expand All @@ -29,6 +28,7 @@
* (optional RIFF chunks)
* */

namespace esp_audio_libs {
namespace wav_decoder {

enum WAVDecoderState {
Expand Down Expand Up @@ -91,5 +91,4 @@ class WAVDecoder {
uint16_t bits_per_sample_ = 0;
};
} // namespace wav_decoder

#endif // WAV_DECODER_H_
} // namespace esp_audio_libs
5 changes: 4 additions & 1 deletion src/decode/flac_decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <esp_heap_caps.h>
#include "flac_decoder.h"

namespace esp_audio_libs {
namespace flac {

FLACDecoderResult FLACDecoder::read_header(uint8_t *buffer, size_t buffer_length) {
Expand Down Expand Up @@ -241,7 +242,8 @@ FLACDecoderResult FLACDecoder::decode_frame_header_() {
return FLAC_DECODER_SUCCESS;
}

FLACDecoderResult FLACDecoder::decode_frame(uint8_t *buffer, size_t buffer_length, int16_t *output_buffer, uint32_t *num_samples) {
FLACDecoderResult FLACDecoder::decode_frame(uint8_t *buffer, size_t buffer_length, int16_t *output_buffer,
uint32_t *num_samples) {
this->buffer_ = buffer;
this->buffer_index_ = 0;
this->bytes_left_ = buffer_length;
Expand Down Expand Up @@ -606,3 +608,4 @@ int64_t FLACDecoder::read_rice_sint(uint8_t param) {
void FLACDecoder::align_to_byte() { this->bit_buffer_length_ -= (this->bit_buffer_length_ % 8); } // align_to_byte

} // namespace flac
} // namespace esp_audio_libs
6 changes: 5 additions & 1 deletion src/decode/mp3_decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@
#include "mp3_decoder.h"

#include <esp_heap_caps.h>
// #include "esphome/core/helpers.h"

namespace esp_audio_libs {
namespace helix_decoder {

/* indexing = [version][samplerate index]
* sample rate of frame (Hz)
Expand Down Expand Up @@ -8851,3 +8853,5 @@ int MP3Decode(HMP3Decoder hMP3Decoder, unsigned char **inbuf, int *bytesLeft, sh
}
return ERR_MP3_NONE;
}
} // namespace helix_decoder
} // namespace esp_audio_libs
2 changes: 2 additions & 0 deletions src/decode/wav_decoder.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "wav_decoder.h"
#include <cstdint>

namespace esp_audio_libs {
namespace wav_decoder {

WAVDecoderResult WAVDecoder::decode_header(uint8_t *buffer, size_t bytes_available) {
Expand Down Expand Up @@ -159,3 +160,4 @@ void WAVDecoder::reset() {
}

} // namespace wav_decoder
} // namespace esp_audio_libs
89 changes: 89 additions & 0 deletions src/dsp/dsps_biquad_f32_ae32.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "dsp_platform.h"
#if (dsps_biquad_f32_ae32_enabled == 1)

// This is bi quad filter form II for ESP32 processor.
.text
.align 4
.global dsps_biquad_f32_ae32
.type dsps_biquad_f32_ae32,@function
// The function implements the following C code:
//esp_err_t dsps_biquad_f32_ae32(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;
// }

dsps_biquad_f32_ae32:
// input - a2
// output - a3
// len - a4
// coeffs - a5
// w- a6

// f0 - b0
// f1 - b1
// f2 - b2
// f3 - a1
// f4 - a2

// f5 - w0
// f6 - w1

entry a1, 16
// Array increment for floating point data should be 4
lsi f0, a5, 0
lsi f1, a5, 4
lsi f2, a5, 8
lsi f3, a5, 12
lsi f4, a5, 16


neg.s f5, f3 // -a[1]
neg.s f6, f4 // -a[2]

lsi f7, a6, 0 // w[0]
lsi f8, a6, 4 // w[1]

addi a3, a3, -4 // i-- // preset a3
lsi f9, a2, 0 // f9 = x[i]
loopnez a4, loop_bq_end_m_ae32
madd.s f9, f7, f5 // f9 += -a1*w0
addi a3, a3, 4 // out++;
mul.s f10, f1, f7 // f10 = b1*w0
madd.s f9, f8, f6 // f9 += -a2*w1
madd.s f10, f9, f0 // f10 += b0*d0
addi a2, a2, 4 // in++;
madd.s f10, f2, f8 // f10+= b2*w1, f10 - result
mov.s f8, f7 // w1 = w0
mov.s f7, f9 // w0 = d0
lsi f9, a2, 0 // f9 = x[i]
ssi f10, a3, 0 // y[i] = result
loop_bq_end_m_ae32:
// Store delay line
ssi f7, a6, 0
ssi f8, a6, 4

movi.n a2, 0 // return status ESP_OK
retw.n

#endif // dsps_biquad_f32_ae32_enabled
Loading

0 comments on commit ccc1197

Please sign in to comment.