Distortion Plugin with variable Oversampling

Hello i run into a problem here.
i got inspired for this way of aproatching oversampling by another question.
it is oversampling an Distortion Algorithm.
All i wanted to do is use the Code from the Question and let the user Pick the Amount of Oversampling desired ." oFactor = [1 2 4 8 16]"
Initialy i wanted to use enum mapping but as it didnt work i simplified the code . the int slider pickes one number of the oFator Array. That way i change the oversampling multiplicator.
The code works with a Constant property but not with one that can be changed. I dont know why.
thank u in advance ;)
Thats the Error i get:
Index in position 1 exceeds array bounds (must not exceed 4).
Error in PickOversampling2/stepImpl (line 70)
xin = disto(plugin.oFactor(plugin.oF)*ii-(plugin.oFactor(plugin.oF)-1):plugin.oFactor(plugin.oF)*endIdx,:);
Error in testbench_PickOversampling2 (line 69)
o1 = step(plugin, in(:,1:2));
Error in validateAudioPlugin
classdef (StrictDefaults)PickOversampling2 < matlab.System & audioPlugin
properties (Constant)
oFactor = [1 2 4 6 16] ;
end
properties (Constant, Hidden)
PluginInterface = audioPluginInterface(...
audioPluginParameter('oF',...
'DisplayName','oF',...
'Label','dB',...
'Mapping',{'int',1,5},...
'Style', 'rotaryknob','Layout', [1 2]))
end
properties
oF = 1
end
properties (Access = private)
Up16;
Down16;
end
methods
% Constructor
function plugin = PickOversampling2
plugin.Up16=dsp.SampleRateConverter;
plugin.Down16=dsp.SampleRateConverter;
calculateSampleRates(plugin);
end
end
methods(Access = protected)
function out = stepImpl(plugin, in)
x = step(plugin.Up16,in);
%Distortion
Drive_lin = 100
x = x * Drive_lin ;
x(x>1)=1 ;
x(x<(-1))=-1 ;
disto=x;
out = zeros(size(in));
for ii = 1:4096:size(in,1)
endIdx = min(ii+4095,size(in,1));
xin = disto(plugin.oFactor(plugin.oF)*ii-(plugin.oFactor(plugin.oF)-1):plugin.oFactor(plugin.oF)*endIdx,:);
assert(size(xin,1)<=plugin.oFactor(plugin.oF)*4096);
out(ii:endIdx,:) = step(plugin.Down16,xin);
end
end
function resetImpl(plugin)
reset(plugin.Up16);
reset(plugin.Down16);
end
function calculateSampleRates(plugin)
sampleRate = 44100;
plugin.Up16.InputSampleRate=sampleRate;
plugin.Up16.OutputSampleRate=plugin.oFactor(plugin.oF)*sampleRate;
plugin.Down16.InputSampleRate=plugin.oFactor(plugin.oF)*sampleRate;
plugin.Down16.OutputSampleRate=sampleRate;
end
end
end

 採用された回答

jibrahim
jibrahim 2021 年 2 月 2 日

1 投票

Hi Adrian,
There are multiple issues here.
First, to reproduce the error, execute this:
h = PickOversampling2
h.oF = 3
h(randn(4,2))
I haven't taken a close look at the code, but there seems to be an indexing error in the for loop. Note that plugins cannot make assumption on the input frame size or sample rate, so we test the plugin with different combinations of inputs sizes and sample rates when we validate it. You validate a plugin with validateAudioPlugin. You can use the -keeptestbench option to preserve a testbench MATLAB file you can run to reproduce and debug issues.
After you resolve this issue in your for loop, you will still have a problerm with your sample rate converters. These objects will not allow their input/output sample rates to be tunable. They must be contants for you to be able to generate a plugin. One solution is to instead use pairs of FIRInterpolator and FIRDecimator objects. Since you can't tune the decimation or interpolation factors, you need to predefine the objects and use the right one depending on the current value of your upsample factor.
This code should work. Note that I skipped the distortion part since it has issues to be resolved.
classdef (StrictDefaults)PickOversampling2 < matlab.System & audioPlugin
properties
OversampleFactor {mustBeMember(OversampleFactor,{'1','2','4','6','16'})} = '1'
end
properties (Access = protected)
poF = 1;
end
properties (Constant, Hidden)
PluginInterface = audioPluginInterface(...
audioPluginParameter('OversampleFactor',...
'DisplayName','Oversample Fsctor','Mapping',{'enum','1','2','4','6','16'}))
end
properties (Access = private)
Up2;
Down2;
Up4;
Down4;
Up6;
Down6;
Up16;
Down16;
pBuff
end
methods
% Constructor
function plugin = PickOversampling2
plugin.Up2 = dsp.FIRInterpolator(2,designMultirateFIR(2,1));
plugin.Up4 = dsp.FIRInterpolator(4,designMultirateFIR(4,1));
plugin.Up6 = dsp.FIRInterpolator(6,designMultirateFIR(6,1));
plugin.Up16 = dsp.FIRInterpolator(16,designMultirateFIR(16,1));
plugin.Down2 = dsp.FIRDecimator(2,designMultirateFIR(1,2));
plugin.Down4 = dsp.FIRDecimator(4,designMultirateFIR(1,4));
plugin.Down6 = dsp.FIRDecimator(6,designMultirateFIR(1,6));
plugin.Down16 = dsp.FIRDecimator(16,designMultirateFIR(1,16));
plugin.pBuff = dsp.AsyncBuffer('Capacity', 192000-1);
end
function set.OversampleFactor(plugin,val)
plugin.OversampleFactor = val;
switch val
case {'2'}
plugin.poF = 2; %#ok
case {'4'}
plugin.poF = 4; %#ok
case {'6'}
plugin.poF = 6; %#ok
case{'16'}
plugin.poF = 16; %#ok
otherwise
plugin.poF = 1; %#ok
end
end
end
methods(Access = protected)
function out = stepImpl(plugin, in)
o = plugin.poF;
switch o
case {2}
x = plugin.Up2(in);
case {4}
x = plugin.Up4(in);
case {6}
x = plugin.Up6(in);
case{16}
x = plugin.Up16(in);
otherwise
x = in;
end
%Distortion
% TO BE ADDED HERE
% workaround the limitation of FIRDecimator (no unbounded
% inputs allowed)
write(plugin.pBuff, x(:,1:size(in,2)));
FrameLength = size(in,1);
z = read(plugin.pBuff, o*FrameLength);
switch o
case {2}
out = plugin.Down2(z);
case {4}
out = plugin.Down4(z);
case {6}
out = plugin.Down6(z);
case{16}
out = plugin.Down16(z);
otherwise
out = x;
end
end
function resetImpl(plugin)
reset(plugin.Up2);
reset(plugin.Down2);
reset(plugin.Up4);
reset(plugin.Down4);
reset(plugin.Up6);
reset(plugin.Down6);
reset(plugin.Up16);
reset(plugin.Down16);
reset(plugin.pBuff);
end
end
end

15 件のコメント

Adrian Alexander
Adrian Alexander 2021 年 2 月 3 日
Hi i realy like ur idea ,
but there is an error.
It does work for x1 x2 x4 but not for x6 and x16.
the error is " Index in position 1 exceeds array bounds( must not exceed 192000)"
Fs of 44100 * 4 = 176400
I guess the Buffer cannot be completly overwritten multible times per second.
is there a work around ? ur idea is almost perfect
jibrahim
jibrahim 2021 年 2 月 3 日
編集済み: jibrahim 2021 年 2 月 3 日
It looks like the buffer capacity is too small for your input size for high upsampling. Try replacing dsp.AsyncBuffer('Capacity', 192000-1) with dsp.AsyncBuffer('Capacity', 4*192000-1) . Replace 4 by the highest number that ensures that 16 * L is smaller than the capacity (L being the max number of samples you feed to the object)
If that does not work, just paste the code that gives you the error, and I can take a look
Adrian Alexander
Adrian Alexander 2021 年 2 月 3 日
編集済み: Adrian Alexander 2021 年 2 月 3 日
very nice it does work exactly as u said it would.
can u tell me why u subtract one ? " ('Capacity', 192000-1)"
is it because 0:192000 generates 192.001 numbers ?
another Question would be why everything must be reset ?
If i get it right resetting occurs when the daw is paused, but in this case the Oversampling doesnt change.
jibrahim
jibrahim 2021 年 2 月 3 日
Hi Adrian,
The subtraction of 1 is a non-intuitive workaround to make code generation pass: If you specify a capacity of 192,000 for the buffer, coder infers that the maximum output of the read function of the buffer is 192,001. This causes issues because 192,001 is not divisble by all your decimation factors. Specifying a capacity of 192,000-1 leads to an inferred max size of 192,000, which is divisble by 2, 4. 6 and 16, so that works.
In the reset method of a plugin, you reset all underlying states of the plugin, which includes states of the underlying decimators/interpolators. Reset is called automatically when the input sample rate of the DAW changes. In this situation you usually want to start fresh from initial conditions. If you need to redesign filters/coefficients based on a new sample rate, that's where you need to do it. Reset is not called when drawing is paused.
This tutorial on audio plugins will answer many of your questions!
Adrian Alexander
Adrian Alexander 2021 年 2 月 3 日
Thank u i will have a look into ur link . there is much to learn all at once ^^
Adrian Alexander
Adrian Alexander 2021 年 2 月 3 日
sry but your code doesnt support code generation however it works in audioTestBench
there is an error when called validateAudioPlugin:
Checking plug-in class 'PickOversamplingMathlab'... passed.
Generating testbench file 'testbench_PickOversamplingMathlab.m'... done.
Running testbench... passed.
Generating mex file 'testbench_PickOversamplingMathlab_mex.mexw64'...
% ??? The number of input rows must be a multiple of the decimation factor.
Error in ==> output Line: 51 Column: 20
Code generation failed: View Error Report
Error using coder.internal.generateAudioPlugin
Error in validateAudioPlugin
I just added more Oversampling Factors the Error occurs both in your version and in my version of the script. something is still missing.
classdef (StrictDefaults)PickOversamplingFinal < matlab.System & audioPlugin
properties
OversampleFactor {mustBeMember(OversampleFactor,{'1','2','4','8','16','32','64'})} = '1'
end
properties (Access = protected)
poF = 1;
end
properties (Constant, Hidden)
PluginInterface = audioPluginInterface(...
audioPluginParameter('OversampleFactor',...
'DisplayName','Oversample Fsctor','Mapping',{'enum','1','2','4','8','16','32','64'}))
end
properties (Access = private)
Up2;
Down2;
Up4;
Down4;
Up8;
Down8;
Up16;
Down16;
Up32;
Down32;
Up64;
Down64;
pBuff
end
methods
% Constructor
function plugin = PickOversamplingFinal
plugin.Up2 = dsp.FIRInterpolator(2,designMultirateFIR(2,1));
plugin.Up4 = dsp.FIRInterpolator(4,designMultirateFIR(4,1));
plugin.Up8 = dsp.FIRInterpolator(8,designMultirateFIR(8,1));
plugin.Up16 = dsp.FIRInterpolator(16,designMultirateFIR(16,1));
plugin.Up32 = dsp.FIRInterpolator(32,designMultirateFIR(32,1));
plugin.Up64 = dsp.FIRInterpolator(64,designMultirateFIR(64,1));
plugin.Down2 = dsp.FIRDecimator(2,designMultirateFIR(1,2));
plugin.Down4 = dsp.FIRDecimator(4,designMultirateFIR(1,4));
plugin.Down8 = dsp.FIRDecimator(8,designMultirateFIR(1,8));
plugin.Down16 = dsp.FIRDecimator(16,designMultirateFIR(1,16));
plugin.Down32 = dsp.FIRDecimator(32,designMultirateFIR(1,32));
plugin.Down64 = dsp.FIRDecimator(64,designMultirateFIR(1,64));
plugin.pBuff = dsp.AsyncBuffer('Capacity', 32 * 192000-1);
end
function set.OversampleFactor(plugin,val)
plugin.OversampleFactor = val;
switch val
case {'2'}
plugin.poF = 2;
case {'4'}
plugin.poF = 4;
case {'8'}
plugin.poF = 8;
case{'16'}
plugin.poF = 16;
case{'32'}
plugin.poF = 32;
case{'64'}
plugin.poF = 64;
otherwise
plugin.poF = 1;
end
end
end
methods(Access = protected)
function out = stepImpl(plugin, in)
o = plugin.poF;
switch o
case {2}
x = plugin.Up2(in);
case {4}
x = plugin.Up4(in);
case {8}
x = plugin.Up8(in);
case{16}
x = plugin.Up16(in);
case{32}
x = plugin.Up32(in);
case{64}
x = plugin.Up64(in);
otherwise
x = in;
end
%Distortion
% TO BE ADDED HERE
% workaround the limitation of FIRDecimator (no unbounded
% inputs allowed)
write(plugin.pBuff, x(:,1:size(in,2)));
FrameLength = size(in,1);
z = read(plugin.pBuff, o*FrameLength);
switch o
case {2}
out = plugin.Down2(z);
case {4}
out = plugin.Down4(z);
case {8}
out = plugin.Down8(z);
case{16}
out = plugin.Down16(z);
case{32}
out = plugin.Down32(z);
case{64}
out = plugin.Down64(z);
otherwise
out = x;
end
end
function resetImpl(plugin)
reset(plugin.Up2);
reset(plugin.Down2);
reset(plugin.Up4);
reset(plugin.Down4);
reset(plugin.Up8);
reset(plugin.Down8);
reset(plugin.Up16);
reset(plugin.Down16);
reset(plugin.Up32);
reset(plugin.Down32);
reset(plugin.Up64);
reset(plugin.Down64);
reset(plugin.pBuff);
end
end
end
jibrahim
jibrahim 2021 年 2 月 3 日
Interesting. The PickOversamplingFinal class you have here passed both validateAudioPlugin and generateAudioPlugin on my Windows machine.
Assuming we are using the exact same plugin, a couple of things that might help?
1) Which release of MATLAB are you using?
2) When you get the error, can you click on the 'View error report' link and see what you get?
Keeping the testbench and attching here might help too
Adrian Alexander
Adrian Alexander 2021 年 2 月 4 日
Hello jibrahim,
I have this version.
'9.8.0.1451342 (R2020a) Update 5'
The Error occurs in
output.m
function varargout = output(obj, varargin)
Errors are :
1
output>6 Line 51 The number of input rows must be a multiple of the decimation factor.
2
output>5 Line 51 The number of input rows must be a multiple of the decimation factor.
3
output>4 Line 51 The number of input rows must be a multiple of the decimation factor.
4
output>3 Line51 The number of input rows must be a multiple of the decimation factor.
5
output>2 Line 51 The number of input rows must be a multiple of the decimation factor.
6
output>1 Line 51The number of input rows must be a multiple of the decimation factor.
SUMMARY
ALL MESSAGES (6)
VARIABLES
I appended the testbench
I hope this information is of any use to u.
maybe i should just update to ur version , if this works with my students licence ^^
jibrahim
jibrahim 2021 年 2 月 4 日
Hi Adrian,
I see this error in R2020a. To work around this, add this line after line 95:
z = z(1:min(32*196000, o*FrameLength),:);
Adrian Alexander
Adrian Alexander 2021 年 2 月 5 日
i applied the rest of my code and everything seems to be working, there is just one last question i have.
I want to remove the DC offset of the distorted Signal after the Oversampling with a HighpassFilter.
%Constructor
plugin.AFilter = dsp.HighpassFilter('SampleRate',44100,'FilterType','IIR','DesignForMinimumOrder',true,'PassbandFrequency',20,'StopbandFrequency',1,'StopbandAttenuation',80);
%methodes
out = step(plugin.AFilter, out);
it works before the Oversampling but not after the Oversampling. i think because ot the switch.
Is there some kind of workaround ? Also i would propably have to construct a filter for common FS(44100,4800) and then apply them with a switch, as FS is not tunable. Is this a good way to apply a non tunable filter?
Generating mex file 'testbench_DistortionPlugin03_mex.mexw64'... ??? When the input is a variable-sized
signal, the number of input channels must remain fixed.
thank u. this is my last question before my project is finished ^^
jibrahim
jibrahim 2021 年 2 月 5 日
Hi Adrian,
If you want to remove DC, I recommend you use the dsp.DCBlocker object, which does not need a SampleRate. You can instantiate the object in the plugin constructor, and use it in stepImpl.
It is also possible to use dsp.HighpassFilter. I recommend you set SampleRate of the highpass filter to a normalized frequency (say 1), and set the other properties (stopband, pass band, etc) to be proportional to that sample rate. That way, your filter is not sensitive to the actual input sample rate of your signal.
Adrian Alexander
Adrian Alexander 2021 年 2 月 6 日
Generating mex file 'testbench_DistortionPlugin03_mex.mexw64'... ??? When the input is a variable-sized signal, the number of input channels must remain fixed.
the error happens with dsp.DCBlocker aswell as with the filter.
%--your Oversampling...
switch o
case {2}
out = plugin.Down2(z);
case {4}
out = plugin.Down4(z);
case {6}
out = plugin.Down6(z);
case{16}
out = plugin.Down16(z);
otherwise
out = x;
end
out = plugin.dcblker(out);
it does work without the oversampling. the switch lets it asume that there is multible inputs. tricky
jibrahim
jibrahim 2021 年 2 月 6 日
The plugin below works for me in 20A. Does it not work for you? If not, I suggest you replace the dc blocking line by:
filterOut = plugin.dcblker(out(:,1:2));
and change the ouput variable name of stepImpl to filterOut.
Here is the plugin I used:
classdef (StrictDefaults)PickOversamplingFinal < matlab.System & audioPlugin
properties
OversampleFactor {mustBeMember(OversampleFactor,{'1','2','4','8','16','32','64'})} = '1'
end
properties (Access = protected)
poF = 1;
end
properties (Constant, Hidden)
PluginInterface = audioPluginInterface(...
audioPluginParameter('OversampleFactor',...
'DisplayName','Oversample Fsctor','Mapping',{'enum','1','2','4','8','16','32','64'}))
end
properties (Access = private)
Up2;
Down2;
Up4;
Down4;
Up8;
Down8;
Up16;
Down16;
Up32;
Down32;
Up64;
Down64;
pBuff
pDCBlocker
end
methods
% Constructor
function plugin = PickOversamplingFinal
plugin.Up2 = dsp.FIRInterpolator(2,designMultirateFIR(2,1));
plugin.Up4 = dsp.FIRInterpolator(4,designMultirateFIR(4,1));
plugin.Up8 = dsp.FIRInterpolator(8,designMultirateFIR(8,1));
plugin.Up16 = dsp.FIRInterpolator(16,designMultirateFIR(16,1));
plugin.Up32 = dsp.FIRInterpolator(32,designMultirateFIR(32,1));
plugin.Up64 = dsp.FIRInterpolator(64,designMultirateFIR(64,1));
plugin.Down2 = dsp.FIRDecimator(2,designMultirateFIR(1,2));
plugin.Down4 = dsp.FIRDecimator(4,designMultirateFIR(1,4));
plugin.Down8 = dsp.FIRDecimator(8,designMultirateFIR(1,8));
plugin.Down16 = dsp.FIRDecimator(16,designMultirateFIR(1,16));
plugin.Down32 = dsp.FIRDecimator(32,designMultirateFIR(1,32));
plugin.Down64 = dsp.FIRDecimator(64,designMultirateFIR(1,64));
plugin.pBuff = dsp.AsyncBuffer('Capacity', 32 * 192000);
plugin.pDCBlocker = dsp.DCBlocker;
end
function set.OversampleFactor(plugin,val)
plugin.OversampleFactor = val;
switch val
case {'2'}
plugin.poF = 2;
case {'4'}
plugin.poF = 4;
case {'8'}
plugin.poF = 8;
case{'16'}
plugin.poF = 16;
case{'32'}
plugin.poF = 32;
case{'64'}
plugin.poF = 64;
otherwise
plugin.poF = 1;
end
end
end
methods(Access = protected)
function out = stepImpl(plugin, in)
o = plugin.poF;
switch o
case {2}
x = plugin.Up2(in);
case {4}
x = plugin.Up4(in);
case {8}
x = plugin.Up8(in);
case{16}
x = plugin.Up16(in);
case{32}
x = plugin.Up32(in);
case{64}
x = plugin.Up64(in);
otherwise
x = in;
end
write(plugin.pBuff, x(:,1:size(in,2)));
FrameLength = size(in,1);
z = read(plugin.pBuff, o*FrameLength);
z = z(1:min(32*196000, o*FrameLength),:);
switch o
case {2}
out = plugin.Down2(z);
case {4}
out = plugin.Down4(z);
case {8}
out = plugin.Down8(z);
case{16}
out = plugin.Down16(z);
case{32}
out = plugin.Down32(z);
case{64}
out = plugin.Down64(z);
otherwise
out = x;
end
% DC Blocker
out = plugin.pDCBlocker(out);
end
function resetImpl(plugin)
reset(plugin.Up2);
reset(plugin.Down2);
reset(plugin.Up4);
reset(plugin.Down4);
reset(plugin.Up8);
reset(plugin.Down8);
reset(plugin.Up16);
reset(plugin.Down16);
reset(plugin.Up32);
reset(plugin.Down32);
reset(plugin.Up64);
reset(plugin.Down64);
reset(plugin.pBuff);
end
end
end
Adrian Alexander
Adrian Alexander 2021 年 2 月 7 日
thank u both works.
i have a lot more code in this plugin.
this solved it.
is there documentation on how and when something like this is needed ?
it fixes the problem but it doesnt realy do anything. the out array is in this format anyway
filterOut = plugin.dcblker(out(:,1:2));
jibrahim
jibrahim 2021 年 2 月 7 日
Hi Adrian,
You are right that the indexing 1:2 does not change the dimensions of the input to the DC blocker. The reason it makes things work is that it helps code generation understand that the number of channels is not variable, but fixed at 2. The decimator outputs a signal that is interpreted as having a variable number of channels (though technically it does not), so we need to workaround this with this extra specification.
This is not documented, but we are aware of this issue and understand that it makes life hard in certain situations. We're working on addressing it.

サインインしてコメントする。

その他の回答 (0 件)

カテゴリ

ヘルプ センター および File ExchangeAudio Plugin Creation and Hosting についてさらに検索

製品

リリース

R2020a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by