DSPRelated.com
Forums

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

Started by nelsona 7 years ago11 replieslatest reply 7 years ago145 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.

We start with the following audio signal (N):

1_6800.png

Convolving audio signal (N) using IR (A), results in the following audio signal:

2_76698.png

Convolving audio signal (N * A) using IR (N - the original signal), results in the following audio signal:

3_18857.png

Convolving audio signal (N * A * N) using IR (A), results in the following audio signal:

4_58097.png

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:

5_18751.png

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

[ - ]
Reply by dszaboMay 7, 2017

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.

[ - ]
Reply by nelsonaMay 7, 2017

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

[ - ]
Reply by dszaboMay 7, 2017

I have no idea how one would draw that conclusion from such an experiment.

[ - ]
Reply by DHMarinovMay 7, 2017

Maybe it has something to do with the way you have written you code. Can you post it?

[ - ]
Reply by nelsonaMay 7, 2017

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

[ - ]
Reply by DHMarinovMay 7, 2017

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

[ - ]
Reply by nelsonaMay 7, 2017

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

[ - ]
Reply by JOSMay 7, 2017

Another thing to watch out for is clipping, for example if N*A*N clips, but N*A doesn't.

[ - ]
Reply by nelsonaMay 7, 2017

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

[ - ]
Reply by JOSMay 7, 2017

Clipping is nonlinear, while convolution is linear. Commutativity applies to linear, time-invariant (LTI) systems.  A clipped convolution is not a convolution.

[ - ]
Reply by nelsonaMay 7, 2017

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