Mathematical associative property of Convolution Reverb does not seem to hold. What am I doing wrong?
Started by 8 years ago●11 replies●latest reply 8 years ago●176 viewsHello,
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
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.
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
I have no idea how one would draw that conclusion from such an experiment.
Maybe it has something to do with the way you have written you code. Can you post it?
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
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).
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
Another thing to watch out for is clipping, for example if N*A*N clips, but N*A doesn't.
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
Clipping is nonlinear, while convolution is linear. Commutativity applies to linear, time-invariant (LTI) systems. A clipped convolution is not a convolution.
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