Mathematical associative property of Convolution Reverb does not seem to hold. What am I doing wrong?

Started by 7 years ago11 replieslatest reply 7 years ago162 views

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.

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).

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.

[ - ]