Avatar

Please consider registering
Guest

sp_LogInOut Log In sp_Registration Register

Register | Lost password?
Advanced Search

— Forum Scope —




— Match —





— Forum Options —





Minimum search word length is 3 characters - maximum search word length is 84 characters

sp_Feed sp_TopicIcon
Nested APFs - Gardner Reverb Help
No permission to create posts
March 5, 2014
1:23 pm
Avatar
mkelvin
Member
Members
Forum Posts: 14
Member Since:
February 22, 2014
sp_UserOfflineSmall Offline

Hey Will,

I'm having a bit of trouble figuring out how to chain nested APFs to make a Gardner-based Reverb. I believe I get it in theory (delay APFs which share the same transverse delay line) but I'm having trouble actually implementing it. Is it possible to do this with the stock CDelayAPFs? Seems I'll need to make new CDelayAPFs for each type of nested scheme.

March 6, 2014
12:07 am
Avatar
rob
Member
Members
Forum Posts: 3
Member Since:
August 27, 2013
sp_UserOfflineSmall Offline

hey mkelvin,
i'm one of will's grad students and i recreated the gardner reverb last year for a project in will's class. i'm a bit rusty on remembering what changes i made here and there to CDelayAPF and CDelay, but i know i changed parts of both. within CDelayAPF i added two additional audio processing functions for inner loop processing, and i added functions for setting the g and delay values for the inner APF (along with new variables to store that data). within CDelay, i believe i added (i say believe because i think it was me who wrote this) functions for reading and writing to locations in the buffer based on time, since the internal APF delays are variable and smaller than the size of the encompassing APF.
let me know if this information helps at all, or if you have more questions based on this. i'd be more than happy to send you any bits of code you're curious about, or explain any of this process further. it's definitely a complex algorithm, and i've implemented functionality for all three room sizes, so feel free to keep asking questions about anything you're confused about, and i'll try to answer any questions you have.
-rob

March 6, 2014
1:19 am
Avatar
mkelvin
Member
Members
Forum Posts: 14
Member Since:
February 22, 2014
sp_UserOfflineSmall Offline

Rob,
Thanks for the reply. Yeah, I think your comment should help me. Will's text implied to me that I ought to be able to do it with the stock objects, but your comment suggests that indeed I need to modify or write my own delay objects, which I should be able to do ok.
I did see that CDelay has a function for reading/writing at arbitrary points within the circular buffer, so that will likely be the function I use for nested APF.

I'll see if I can figure out tonight how to modify CDelayAPF to support nested APFs and post my results.

March 6, 2014
1:24 am
Avatar
W Pirkle
Admin
Forum Posts: 143
Member Since:
January 28, 2017
sp_UserOfflineSmall Offline

thanks rob for the reply!

Gardner's reverbs are well known in part because Bill Gardner is well known but also because at the time he wrote his thesis (1992) most companies that made reverb units were very tight-lipped about their algorithms (and still are) so it was interesting to have this in the literature. I did some experimenting with them (the figures from my book are small nested APFs but I did the complete small-room reverb as an exercise as well as several variations) and found them to be generally metallic as well. They are also extremely sensitive to loop parameters and can go unstable easily (they are nested feedback structures...). You can also get interesting but unpleasant "anti-reverb" where the reverb builds and builds until it explodes.

Here's a link to Gardner's Master's Thesis paper:

http://www.media.mit.edu/~bill.....esis.ps.gz

- Will

March 6, 2014
1:27 am
Avatar
W Pirkle
Admin
Forum Posts: 143
Member Since:
January 28, 2017
sp_UserOfflineSmall Offline

Oh and yeah, I also modified the CDelayAPF massively/kludge - so much that I thought I should make a new class but I got sidetracked and never got back to it.

- Wp

March 6, 2014
2:34 am
Avatar
rob
Member
Members
Forum Posts: 3
Member Since:
August 27, 2013
sp_UserOfflineSmall Offline

great! let me know what you get working. the audio processing for the nested APF should be similar to the main APF processaudio function, and the main function should call the audio processing functions of the nested. okay, as long as the function to read/write to specific locations is there, you should be good, but i'll double check later to see if i did anything else in CDelay because i believe i did some work there.

March 7, 2014
7:11 am
Avatar
mkelvin
Member
Members
Forum Posts: 14
Member Since:
February 22, 2014
sp_UserOfflineSmall Offline

Ok, haven't yet got a chance to test it yet, but would something like this make sense for a N-count nested APF class?

My only question is the order of operations in the processAudio loop -- should I process the nested APFs first, before the outer APF?

Note, I wrote a new method for CDelay which lets me write at arbitrary points in the delay line:

void CDelay::writeDelayAt(float fmSec, float fDelayInput)
{
float fDelayInSamples = fmSec *((float)m_nSampleRate)/1000.0f;
int nWriteIndex = m_nWriteIndex - (int)fDelayInSamples;

// note: while is better than a single "if" because fmSec could be many multiples past our buffer size...
while (nWriteIndex = m_nBufferSize)
nWriteIndex -= m_nBufferSize;

// write to the delay line
m_pBuffer[nWriteIndex] = fDelayInput; // external feedback sample
}

class CDelayAPFNested : public CDelay
{
public:
CDelayAPFNested(unsigned numNestedAPFs);
~CDelayAPFNested(void);

struct NestedAPF {
float f_g;
float f_delayStart;
float f_delayEnd;
};

// members
protected:
float m_fAPF_g; // one g coefficient
NestedAPF * m_NestedApfs; // N number of nested APFs
unsigned m_nNumNestedApfs;

void setNestedApfInfo(unsigned nIndex, const NestedAPF & info);
void setAPF_g(float fAPFg){m_fAPF_g = fAPFg;}

// overrides
bool processAudio(float* pInput, float* pOutput);

// private helpers
private:
void processAPF(float f_g, float xn, float fw_n_D, float * fw_n, float * yn);
float processNestedAPF(unsigned nIndex);
};

CDelayAPFNested::CDelayAPFNested(unsigned numNestedApfs)
: m_nNumNestedApfs(numNestedApfs),
m_NestedApfs(0)
{
m_NestedApfs = new NestedAPF[numNestedApfs];
memset((void *)m_NestedApfs , 0, sizeof(NestedAPF)*m_nNumNestedApfs);
}

CDelayAPFNested::~CDelayAPFNested(void)
{
if (m_NestedApfs )
delete[] m_NestedApfs;

}

void CDelayAPFNested::setNestedApfInfo(unsigned nIndex, const NestedAPF & info)
{
if (nIndex 0.0 && *fy_n < FLT_MIN_PLUS) *fy_n = 0.0f;
if(*fy_n FLT_MIN_MINUS) *fy_n = 0.0f;
}

float CDelayAPFNested::processNestedAPF(unsigned nIndex)
{
// error! can't process this nested apf index
if (nIndex >= m_nNumNestedApfs)
return 0.0f;

const NestedAPF & nestedApf = m_NestedApfs[nIndex];

// read delay line end/start of nested APF
float fw_n_D = this->readDelayAt(nestedApf.f_delayEnd);
float fx_n = this->readDelayAt(nestedApf.f_delayStart);

// perform the APF calculations
float fw_n = 0.0f, fy_n = 0.0f;
processAPF(nestedApf.f_g, fx_n, fw_n_D, &fw_n, &fy_n);

this->writeDelayAt(nestedApf.f_delayStart, fy_n);

}

bool CDelayAPFNested::processAudio(float* pInput, float* pOutput)
{
// for the outer APF if the delay is 0.0
// need to pass input -> output
if(m_nReadIndex == m_nWriteIndex)
{
this->writeDelayAndInc(*pInput);
*pOutput = *pInput;
return true;
}

// first perform the outer APF calculations first

// read the delay line to get w(n-D); call base class
// this is getting the outer-most APF
float fw_n_D = this->readDelay();
float fw_n = 0.0f, fy_n = 0.0f;
processAPF(m_fAPF_g, *pInput, fw_n_D, &fw_n, &fy_n);
// write outer delay line
this->writeDelayAndInc(fw_n);

// now loop through our nested APFs and process them
for (unsigned i = 0; i < m_nNumNestedApfs; ++i)
processNestedAPF(i);

// write the output sample (could be combined with above line)
*pOutput = fy_n;

}

March 7, 2014
7:11 am
Avatar
mkelvin
Member
Members
Forum Posts: 14
Member Since:
February 22, 2014
sp_UserOfflineSmall Offline

BTW, your forum strips spaces... which makes the code a lot more ugly. :(

March 7, 2014
7:41 am
Avatar
mkelvin
Member
Members
Forum Posts: 14
Member Since:
February 22, 2014
sp_UserOfflineSmall Offline

Actually, could probably do the nested code inside the CDelayAPF and just not set any of the nested APFs for the normal APF case (i.e. default constructor would just set m_nNumNestedAPFs to 0). Doesn't look like I'm doing too much beyond the normal CDelayAPF.

Also, the procesNestedAPF function I wrote up there shouldn't return a float.

And somehow the copy-paste messed up two functions -- setNestedApfInfo and processAPF. For some reason, the forum is munging the copy-paste for these two functions.... I've repasted it in here like 5 times. Looks good, then when i hit submit, it messes up the function -- not just without spacing, like it's cutting entire chunks out of the code.

Ah... after the 6th attempt, I see what the issue is! The forum doesn't like "greater than" or "less than" symbols... the "less than" symbol is somehow cutting out code until ti sees another "greater than" symbol".

Here's the code with the comparison operators replaced. You should consider installing a code-editor widget in your wordpress site... :D

void CDelayAPFNested::setNestedApfInfo(unsigned nIndex, const NestedAPF & info)
{
if (nIndex LESS_THAN m_nNumNestedApfs)
m_NestedApfs[nIndex] = info;

}

void CDelayAPFNested::processAPF(float f_g, float xn, float fw_n_D, float * fw_n, float * fy_n)
{
// form w(n) = x(n) + gw(n-D)
*fw_n = xn + f_g*fw_n_D;

// form y(n) = -gw(n) + w(n-D)
*fy_n = -f_g*(*fw_n) + fw_n_D;

// underflow check
if(*fy_n GREATER_THAN 0.0 && *fy_n LESS_THAN FLT_MIN_PLUS) *fy_n = 0.0f;
if(*fy_n LESS_THAN 0.0 && *fy_n GREATER_THAN FLT_MIN_MINUS) *fy_n = 0.0f;
}

March 7, 2014
8:02 am
Avatar
mkelvin
Member
Members
Forum Posts: 14
Member Since:
February 22, 2014
sp_UserOfflineSmall Offline

Ok, I posted the above code in pastie:

http://pastie.org/8885688

March 18, 2014
6:13 am
Avatar
rob
Member
Members
Forum Posts: 3
Member Since:
August 27, 2013
sp_UserOfflineSmall Offline

i'm really sorry about not seeing this sooner. i've been on spring break and in complete thesis writing mode. i will take a look at your replies and code tomorrow.

March 18, 2014
5:45 pm
Avatar
W Pirkle
Admin
Forum Posts: 143
Member Since:
January 28, 2017
sp_UserOfflineSmall Offline

BTW I have added the TinyMCE editor and the Syntax Highlight plugin (which is supposed to format code correctly) to Simple:Press but I still get my tabs stripped too... its a Simple:Press issue, not a WordPress thing - right now I am racing after a publishing deadline so only have time to maintain the site periodically - if you figure out how to make the syntax highlight plugin work, let me know.

 

Will

Forum Timezone: America/New_York

Most Users Ever Online: 36

Currently Online:
3 Guest(s)

Currently Browsing this Page:
1 Guest(s)

Top Posters:

Skyler: 47

Peter: 41

Derek: 41

clau_ste: 39

Frodson: 38

Gwen: 32

EZB: 24

lppier: 23

Msaldaña: 18

Jorge: 17

Member Stats:

Guest Posters: 1

Members: 476

Moderators: 1

Admins: 4

Forum Stats:

Groups: 11

Forums: 30

Topics: 482

Posts: 1876

Newest Members:

sam, annaharris, Marie Weaver, kev, Steven, Mr Anderson, mguy, omelc

Moderators: W Pirkle: 143

Administrators: Tom: 65, JD Young: 80, Will Pirkle: 0, W Pirkle: 143