Mathematical associative property of Convolution Reverb does not seem to hold. What am I doing wrong?
![](https://www.embeddedrelated.com/new/images/defaultavatar.jpg)
Hello,
While testing some theories out in regards to convolution reverb, I noticed something odd which I wasn't able to explain. I was hoping someone could clarify what exactly I was doing wrong / not understanding.
We start with the following audio signal (N):
Convolving audio signal (N) using IR (A), results in the following audio signal:
Convolving audio signal (N * A) using IR (N - the original signal), results in the following audio signal:
Convolving audio signal (N * A * N) using IR (A), results in the following audio signal:
So far nothing seems out of the ordinary.
However convolving (N * A - from step 2) with itself (N * A), results in the following audio signal:
Given that convolution is associative, why doesn't N * A * N * A equal (N * A) * (N * A)?
Apologies for all the questions and a big thank you for the help.
Nelson
![](https://www.embeddedrelated.com/new/images/defaultavatar.jpg)
You need to make sure that your output is long enough to store the result of the convolutions. Convolution of a signal length M and a signal length N results in a signal length N+M-1 (am I right on the minus one?), try padding your signals with a bunch of zeros (i.e. silence) and repeating the experiment.
![](https://www.embeddedrelated.com/new/images/defaultavatar.jpg)
Hello Dszabo,
Interesting point (as I didn't think about that).
Quick question, if I see with a padding of zeroes (or silence), that everything works (i.e. N * A * N * A = (N * A) * (N * A)), will that mean that it would be impossible for someone to derive N * A * N * A if they only had N * A (cut off in a similar way as depicted in the images from my original post)?
Thank you very much for all your help,
Nelson
![](https://www.embeddedrelated.com/new/images/defaultavatar.jpg)
I have no idea how one would draw that conclusion from such an experiment.
![](https://d23s79tivgl8me.cloudfront.net/user/profilepictures/111911.jpg)
Maybe it has something to do with the way you have written you code. Can you post it?
![](https://www.embeddedrelated.com/new/images/defaultavatar.jpg)
Hello DHMarinov,
I was using Audacity (a digital audio workstation) with FreeVerb (an audio convolution plugin / VST).
As for FreeVerb, it is open source.
To simplify things, I will only post the *.hpp, *.cpp and main for FreeVerb.
Any help would be greatly appreciated.
Thank you,
Nelson
#HPP#####################################################################
// Freeverb3 user interface declaration
// Based on Steinberg VST Development Kit Examples
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
#ifndef __Freeverb_H
#define __Freeverb_H
#include "audioeffectx.h"
#include "revmodel.hpp"
enum
{
KMode, KRoomSize, KDamp, KWidth, KWet, KDry,
KNumParams
};
class Freeverb : public AudioEffectX
{
public:
Freeverb(audioMasterCallback audioMaster);
virtual void process(float **inputs, float **outputs, long sampleFrames);
virtual void processReplacing(float **inputs, float **outputs, long sampleFrames);
virtual void setProgramName(char *name);
virtual void getProgramName(char *name);
virtual void setParameter(long index, float value);
virtual float getParameter(long index);
virtual void getParameterLabel(long index, char *label);
virtual void getParameterDisplay(long index, char *text);
virtual void getParameterName(long index, char *text);
virtual void suspend();
virtual void resume();
virtual bool getEffectName (char* name);
virtual bool getVendorString (char* text);
virtual bool getProductString (char* text);
virtual long canDo(char* text);
private:
revmodel model;
char programName[32];
};
#endif//_Freeverb_H
//ends
#CPP#####################################################################
// Freeverb3 user interface implementation
// Based on Steinberg VST Development Kit Examples
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
#include "Freeverb.hpp"
Freeverb::Freeverb(audioMasterCallback audioMaster)
: AudioEffectX(audioMaster, 1, KNumParams) // 1 program
{
setNumInputs(2); // stereo in
setNumOutputs(2); // stereo out
setUniqueID('JzR3'); // identify - CHANGE THIS TO MAKE YOUR OWN!!!
canMono(); // makes sense to feed both inputs with the same signal
canProcessReplacing(); // supports both accumulating and replacing output
strcpy(programName, "Default"); // default program name
}
void Freeverb::suspend()
{
model.mute();
}
void Freeverb::resume()
{
model.mute();
}
bool Freeverb::getEffectName (char* name)
{
strcpy (name, "Freeverb3"); // Change this to what you want!!
return true;
}
bool Freeverb::getVendorString (char* text)
{
strcpy (text, "Dreampoint"); // Change this to what you want!!
return true;
}
bool Freeverb::getProductString (char* text)
{
strcpy (text, "Freeverb3"); // Change this to what you want!!
return true;
}
long Freeverb::canDo (char* text)
{
if (!strcmp (text, "1in1out"))
return 1;
if (!strcmp (text, "2in2out"))
return 1;
if (!strcmp (text, "1in2out"))
return 1;
return -1;
}
void Freeverb::setProgramName(char *name)
{
strcpy(programName, name);
}
void Freeverb::getProgramName(char *name)
{
strcpy(name, programName);
}
void Freeverb::setParameter(long index, float value)
{
switch (index)
{
case KMode:
model.setmode(value);
break;
case KRoomSize:
model.setroomsize(value);
break;
case KDamp:
model.setdamp(value);
break;
case KWet:
model.setwet(value);
break;
case KDry:
model.setdry(value);
break;
case KWidth:
model.setwidth(value);
break;
}
}
float Freeverb::getParameter(long index)
{
float ret;
switch (index)
{
case KMode:
ret = model.getmode();
break;
case KRoomSize:
ret = model.getroomsize();
break;
case KDamp:
ret = model.getdamp();
break;
case KWet:
ret = model.getwet();
break;
case KDry:
ret = model.getdry();
break;
case KWidth:
ret = model.getwidth();
break;
}
return ret;
}
void Freeverb::getParameterName(long index, char *label)
{
switch (index)
{
case KMode:
strcpy(label, "Mode");
break;
case KRoomSize:
strcpy(label, "Room size");
break;
case KDamp:
strcpy(label, "Damping");
break;
case KWet:
strcpy(label, "Wet level");
break;
case KDry:
strcpy(label, "Dry level");
break;
case KWidth:
strcpy(label, "Width");
break;
}
}
void Freeverb::getParameterDisplay(long index, char *text)
{
switch (index)
{
case KMode:
if (model.getmode() >= freezemode)
strcpy(text,"Freeze");
else
strcpy(text,"Normal");
break;
case KRoomSize:
float2string(model.getroomsize()*scaleroom+offsetroom, text);
break;
case KDamp:
long2string((long)(model.getdamp()*100), text);
break;
case KWet:
dB2string(model.getwet()*scalewet,text);
break;
case KDry:
dB2string(model.getdry()*scaledry,text);
break;
case KWidth:
long2string((long)(model.getwidth()*100), text);
break;
}
}
void Freeverb::getParameterLabel(long index, char *label)
{
switch (index)
{
case KMode:
strcpy(label,"mode");
break;
case KRoomSize:
strcpy(label,"size");
break;
case KDamp:
case KWidth:
strcpy(label, "%");
break;
case KWet:
case KDry:
strcpy(label, "dB");
break;
}
}
void Freeverb::process(float **inputs, float **outputs, long sampleFrames)
{
model.processmix(inputs[0],inputs[1],outputs[0],outputs[1],sampleFrames,1);
}
void Freeverb::processReplacing(float **inputs, float **outputs, long sampleFrames)
{
model.processreplace(inputs[0],inputs[1],outputs[0],outputs[1],sampleFrames,1);
}
//ends
#Main####################################################################
// Freeverb3 initialisation implementation
// Based on Steinberg VST Development Kit Examples
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// This code is public domain
#include "Freeverb.hpp"
static AudioEffect *effect = NULL;
bool oome = false;
#if MAC
#pragma export on
#endif
// prototype of the export function main
#if BEOS
#define main main_plugin
extern "C" __declspec(dllexport) AEffect *main_plugin (audioMasterCallback audioMaster);
#else
AEffect *main (audioMasterCallback audioMaster);
#endif
AEffect *main (audioMasterCallback audioMaster)
{
// get vst version
if (!audioMaster (0, audioMasterVersion, 0, 0, 0, 0))
return 0; // old version
effect = new Freeverb (audioMaster);
if (!effect)
return 0;
if (oome)
{
delete effect;
return 0;
}
return effect->getAeffect ();
}
#if MAC
#pragma export off
#endif
#if WIN32
#include <windows.h>
void* hInstance;
BOOL WINAPI DllMain (HINSTANCE hInst, DWORD dwReason, LPVOID lpvReserved)
{
hInstance = hInst;
return 1;
}
#endif
//ends
![](https://d23s79tivgl8me.cloudfront.net/user/profilepictures/111911.jpg)
Hmmm, no idea, but then why would you want to convolve the output of the reverb by itself? As far as I have experimented with audio, that won't bring any pleasant effects (unless you aim for it :D).
![](https://www.embeddedrelated.com/new/images/defaultavatar.jpg)
Hello DHMarinov,
I was actually using reverb in a non-musical application.
Unfortunately, it looks like reverb won't work in the way I hoped it would.
Either way, thank you for your help.
Nelson
![](https://d23s79tivgl8me.cloudfront.net/user/profilepictures/33457.jpg)
Another thing to watch out for is clipping, for example if N*A*N clips, but N*A doesn't.
![](https://www.embeddedrelated.com/new/images/defaultavatar.jpg)
Hello JOS,
Given that Convolution is both mathematically commutative and associative, would clipping really matter?
I was under the impression that clipping was just a part of the convolution, and had no negative effects on the convolution process itself (though I could be wrong).
Thank you for the input,
Nelson
![](https://d23s79tivgl8me.cloudfront.net/user/profilepictures/33457.jpg)
Clipping is nonlinear, while convolution is linear. Commutativity applies to linear, time-invariant (LTI) systems. A clipped convolution is not a convolution.
![](https://www.embeddedrelated.com/new/images/defaultavatar.jpg)
Hello JOS,
My apologies, I was confusing clipping for phase cancellation.
I absolutely agree about clipping being non-linear / negatively interfering with convolution.
Thank you,
Nelson