Engee documentation
Notebook

Parametric Audio Equaliser

In this demonstration we will understand a parametric audio equaliser that supports three adjustable bands. Each band is implemented using a different biquadratic filter structure. image.png

An equaliser is a radio-electronic device or computer program that allows selective correction of signal amplitude depending on frequency characteristics.

Also in this demonstration we will compare the work of Engee and MATLAB models. For this purpose we will connect the necessary libraries and consider the structure of the implemented model.

In [ ]:
Pkg.add(["Statistics", "CSV"])
   Resolving package versions...
  No Changes to `~/.project/Project.toml`
  No Changes to `~/.project/Manifest.toml`
In [ ]:
using Plots
using MATLAB
using CSV
using DataFrames
using Statistics
In [ ]:
plotlyjs();
In [ ]:
mat"start_simulink"
mat"p = genpath('/user/start/examples'); addpath(p);"

The model consists of two main blocks: the equaliser itself, which includes 3 filtering blocks with filter coefficients at the upper level, and the discrete transfer function estimator. Figures below show the model and estimator settings.

image.png

image_2.png

If we consider each of the filters, they are implemented in the same way and include a discrete filter coefficient sampling unit and a biquadratic filter.

image_3.png

Now let us proceed to running and comparing the implementation of this model in Engee and Simulink.

Running and analysing the Engee model

Let's take a look at the results of modelling in Engee:

In [ ]:
function rum_model(NemeModel,Path_to_folder)
Path = Path_to_folder * "/" * NemeModel * ".engee"
   if NemeModel in [m.name for m in engee.get_all_models()] # Проверка условия загрузки модели в ядро
      model = engee.open(NemeModel) # Открыть модель
      engee.run(model, verbose=true); # Запустить модель
   else
      model = engee.load(Path, force=true) # Загрузить модель
      engee.run(model, verbose=true); # Запустить модель
      engee.close(NemeModel, force=true); # Закрыть модель
   end
end
Out[0]:
rum_model (generic function with 1 method)
In [ ]:
# Запуск модели
rum_model("Equalizer","/user/start/examples/dsp/equalizer")
Building...
Progress 0%
Progress 1%
Progress 32%
Progress 100%
Progress 100%
In [ ]:
function parse_csv_engee(filename :: String)
    file = open(filename)
    line = readline(file)
    name_idx = vcat(0,findall(',', line), length(line)+1)
    names = collect([line[(name_idx[it-1]+1):(name_idx[it]-1)] for it  2:length(name_idx)])
    data = collect([it for it  (line -> eval(Meta.parse(line)))(readline(file))])
    while !eof(file)
        data = ((a, b) -> [a;;;b]).(data, collect([it for it  (line -> eval(Meta.parse(line)))(readline(file))]))
    end
    close(file)
    return Dict(names .=> data)
end
Out[0]:
parse_csv_engee (generic function with 1 method)
In [ ]:
# Чтение CSV
out = parse_csv_engee("/user/start/examples/dsp/equalizer/out.csv");
out_t = out["time"];
out_data = out["1"];
In [ ]:
# Построение графиков
Eng_data = reshape(out_data,808)
plot(Eng_data[1:8:end])
plot!(Eng_data[2:8:end])
plot!(Eng_data[3:8:end])
plot!(Eng_data[4:8:end])
plot!(Eng_data[5:8:end])
plot!(Eng_data[6:8:end])
plot!(Eng_data[7:8:end])
plot!(Eng_data[8:8:end])
plot!(title = "Результаты моделирования в Engee", ylabel = "Данные", xlabel="Время, c")
Out[0]:

Let's compare the simulation results in Engee and Simulink. To do this, let's first run a simulation of the model.

In [ ]:
mat"run_test_model('Equalizer')";

Let's look at the results in Simulink:

In [ ]:
mat"whos" # Вывести все переменные, хранящиеся в рабочей области
>> >> >>   Name                     Size                  Bytes  Class                             Attributes

  SimulationSteps        101x1                     808  double                                      
  SysOutput                1x1                    6800  timeseries                                  
  a                        1x6                      48  double                                      
  b                        1x6                      48  double                                      
  matlab_jl_has_ans        1x1                       8  double                                      
  n                        1x32                    256  double                                      
  out                      1x1                  847601  Simulink.SimulationOutput                   
  p                        1x585978            1171956  char                                        
  sig                      1x1                    7018  Simulink.SimulationData.Signal              

In [ ]:
# Чтение выхода модели
sim = mat"SysOutput.Data";
In [ ]:
# Построение графиков
Sim_data = reshape(sim,808)
plot(Sim_data[1:8:end])
plot!(Sim_data[2:8:end])
plot!(Sim_data[3:8:end])
plot!(Sim_data[4:8:end])
plot!(Sim_data[5:8:end])
plot!(Sim_data[6:8:end])
plot!(Sim_data[7:8:end])
plot!(Sim_data[8:8:end])
plot!(title = "Результаты моделирования в Simulink", ylabel = "Отклик", xlabel="Время, c")
Out[0]:

Now plot the difference between the outputs of Simulink and Engee, and calculate the average error.

In [ ]:
plot(Sim_data-Eng_data)
Out[0]:
In [ ]:
print("Погрешность: " * string((sum(abs.(Sim_data-Eng_data)))/length(Eng_data)))
Погрешность: 2.3758486926991814e-12

As we can see from the graphs above and the result of the average error calculation, the models work almost identically.

Conclusion

In this example, we have implemented EQ and compared the capabilities of Engee with respect to modelling in Simulink and shown the options for connecting the MATLAB kernel to solve problems within the Engee development environment.