Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add minimal PhISEM (Shaker) physical model #18

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions leaf/Inc/leaf-physical.h
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,71 @@ typedef struct _tStiffString
void tStiffString_setDecayNoUpdate(tStiffString* const, Lfloat decay);
void tStiffString_setDecayHighFreqNoUpdate(tStiffString* const, Lfloat decayHF);

//==============================================================================
/*!
@defgroup tshader tShaker
@ingroup physical
@brief PhISEM class - borrowed from STK
@{

@fn void tShaker_init (tShaker* const, Lfloat offset, Lfloat slope, LEAF* const leaf)
@brief Initialize a tShaker to the default mempool of a LEAF instance.
@param shaker A pointer to the tShaker to initialize.
@param leaf A pointer to the leaf instance.

@fn void tShaker_initToPool (tShaker* const, Lfloat offset, Lfloat slope, tMempool* const)
@brief Initialize a tShaker to a specified mempool.
@param shaker A pointer to the tShaker to initialize.
@param mempool A pointer to the tMempool to use.

@fn void tShaker_free (tShaker* const)
@brief Free a tShaker from its mempool.
@param shaker A pointer to the tShaker to free.

@fn Lfloat tShaker_tick (tShaker* const, Lfloat input)
@brief
@param shaker A pointer to the relevant tShaker.

@fn Lfloat tShaker_excite (tShaker* const, Lfloat energy)
@brief Excite the system with the given energy.
@param shaker A pointer to the relevant tShaker.

@} */

typedef struct _tShaker
{
tMempool mempool;
Lfloat(*rand)(void);

tNoise noise;
tBiQuad resonator;

Lfloat energyDecay;
Lfloat soundDecay;
Lfloat probability;
Lfloat gain;

Lfloat energy;
Lfloat level;

} _tShaker;

typedef _tShaker *tShaker;

void tShaker_init(tShaker *const, LEAF *const leaf);
void tShaker_initToPool(tShaker *const, tMempool *const);
void tShaker_free(tShaker *const);

Lfloat tShaker_tick(tShaker *const);
Lfloat tShaker_excite(tShaker *const, Lfloat energy);

void tShaker_setEnergyDecay(tShaker *const, Lfloat decay);
void tShaker_setSoundDecay(tShaker *const, Lfloat decay);
void tShaker_setProbability(tShaker *const, Lfloat prob);
void tShaker_setResonance (tShaker *const pm, Lfloat freq, Lfloat radius);

//==============================================================================

#ifdef __cplusplus
}
#endif
Expand Down
93 changes: 93 additions & 0 deletions leaf/Src/leaf-physical.c
Original file line number Diff line number Diff line change
Expand Up @@ -3985,3 +3985,96 @@ void tStiffString_pluckNoUpdate(tStiffString* const mp, Lfloat amp)
}



/// Shaker model

void tShaker_init(tShaker* const pm, LEAF* const leaf)
{
tShaker_initToPool(pm, &leaf->mempool);
}

void tShaker_initToPool(tShaker* const pm, tMempool* const mp)
{
_tMempool* m = *mp;
_tShaker* p = *pm = (_tShaker*) mpool_alloc(sizeof(_tShaker), m);
p->mempool = m;

LEAF* leaf = p->mempool->leaf;
p->rand = leaf->random;

tBiQuad_initToPool(&p->resonator, mp);
tBiQuad_setResonance(&p->resonator, 4500., 0.99, false);
tNoise_initToPool(&p->noise, WhiteNoise, mp);

p->energy = 0.0;
p->level = 0.0;
p->energyDecay = 0.98;
p->soundDecay = 0.95;
p->probability = 0.10;
}

void tShaker_free(tShaker* const pm)
{
_tShaker* p = *pm;

mpool_free((char*)p, p->mempool);
}

Lfloat tShaker_tick(tShaker* const pm)
{
_tShaker* p = *pm;

// exponential decay for system energy
p->energy *= p->energyDecay;

// stochastic particle collision event
if(p->rand() < p->probability)
p->level += p->energy * p->gain;

// exponential decay for sound
p->level *= p->soundDecay;

// add noise, filter
Lfloat sample = p->level * tNoise_tick(&p->noise);
Lfloat output = tBiQuad_tick(&p->resonator, sample);

return output;
}

Lfloat tShaker_excite (tShaker* const pm, Lfloat energy)
{
_tShaker* p = *pm;

// excite the system
p->energy += energy;
return p->energy;
}

void tShaker_setEnergyDecay (tShaker *const pm, Lfloat decay)
{
_tShaker* p = *pm;
p->energyDecay = decay;
}

void tShaker_setSoundDecay (tShaker *const pm, Lfloat decay)
{
_tShaker* p = *pm;
p->soundDecay = decay;
}

void tShaker_setProbability (tShaker *const pm, Lfloat prob)
{
_tShaker* p = *pm;

// set gain correction for number of particles
float nBeans = 1024.0 * prob;
float gain = (log(nBeans) / log(4.)) * (40/nBeans);

p->probability = prob;
p->gain = gain;
}

void tShaker_setResonance (tShaker *const pm, Lfloat freq, Lfloat radius) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that getting rid of the resonator field entirely, and instead returning the direct output p->level * tNoise_tick(&p->noise) on each tick, would make for a better API. This is fine because there's no feedback.

If we let the user handle the filtering/resonance, they can set up and modify as many resonating stages as they like (which is used on almost all of Cook's PhISEM presets), and we don't have to expose a wrapper to tBiQuad_setResonance for tShaker.

_tShaker* p = *pm;
tBiQuad_setResonance(&p->resonator, freq, radius, false);
}