Skip to content

Commit

Permalink
Merge pull request #3 from ywwg/features_effects-reverb
Browse files Browse the repository at this point in the history
Features effects reverb
  • Loading branch information
rryan committed Jan 5, 2014
2 parents edb2e43 + a142872 commit 8ec6a42
Show file tree
Hide file tree
Showing 6 changed files with 365 additions and 6 deletions.
1 change: 1 addition & 0 deletions build/depends.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ def sources(self, build):
"effects/native/bitcrushereffect.cpp",
"effects/native/flangereffect.cpp",
"effects/native/filtereffect.cpp",
"effects/native/reverbeffect.cpp",

"engine/effects/engineeffectsmanager.cpp",
"engine/effects/engineeffectchain.cpp",
Expand Down
20 changes: 14 additions & 6 deletions src/effects/effectsmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,25 +97,33 @@ void EffectsManager::setupDefaults() {
this, "org.mixxx.effectchain.flanger"));
pChain->setName(tr("Flanger"));
pChain->setParameter(0.0f);
EffectPointer flanger = instantiateEffect(
EffectPointer pEffect = instantiateEffect(
"org.mixxx.effects.flanger");
pChain->addEffect(flanger);
pChain->addEffect(pEffect);
m_pEffectChainManager->addEffectChain(pChain);

pChain = EffectChainPointer(new EffectChain(
this, "org.mixxx.effectchain.bitcrusher"));
pChain->setName(tr("BitCrusher"));
pChain->setParameter(0.0f);
flanger = instantiateEffect("org.mixxx.effects.bitcrusher");
pChain->addEffect(flanger);
pEffect = instantiateEffect("org.mixxx.effects.bitcrusher");
pChain->addEffect(pEffect);
m_pEffectChainManager->addEffectChain(pChain);

pChain = EffectChainPointer(new EffectChain(
this, "org.mixxx.effectchain.filter"));
pChain->setName(tr("Filter"));
pChain->setParameter(0.0f);
flanger = instantiateEffect("org.mixxx.effects.filter");
pChain->addEffect(flanger);
pEffect = instantiateEffect("org.mixxx.effects.filter");
pChain->addEffect(pEffect);
m_pEffectChainManager->addEffectChain(pChain);

pChain = EffectChainPointer(new EffectChain(
this, "org.mixxx.effectchain.reverb"));
pChain->setName(tr("Reverb"));
pChain->setParameter(0.0f);
pEffect = instantiateEffect("org.mixxx.effects.reverb");
pChain->addEffect(pEffect);
m_pEffectChainManager->addEffectChain(pChain);
}

Expand Down
2 changes: 2 additions & 0 deletions src/effects/native/nativebackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
#include "effects/native/flangereffect.h"
#include "effects/native/bitcrushereffect.h"
#include "effects/native/filtereffect.h"
#include "effects/native/reverbeffect.h"

NativeBackend::NativeBackend(QObject* pParent)
: EffectsBackend(pParent, tr("Native")) {
registerEffect<FlangerEffect>();
registerEffect<BitCrusherEffect>();
registerEffect<FilterEffect>();
registerEffect<ReverbEffect>();
}

NativeBackend::~NativeBackend() {
Expand Down
136 changes: 136 additions & 0 deletions src/effects/native/reverbeffect.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#include <QtDebug>

#include "effects/native/reverbeffect.h"
#include "effects/native/waveguide_nl.h"

#include "mathstuff.h"
#include "sampleutil.h"

#define RUN_WG(n, junct_a, junct_b) waveguide_nl_process_lin(group_state.waveguide[n], junct_a - group_state.out[n*2+1], junct_b - group_state.out[n*2], group_state.out+n*2, group_state.out+n*2+1)

// static
QString ReverbEffect::getId() {
return "org.mixxx.effects.reverb";
}

// static
EffectManifest ReverbEffect::getManifest() {
EffectManifest manifest;
manifest.setId(getId());
manifest.setName(QObject::tr("Reverb"));
manifest.setAuthor("The Mixxx Team, SWH Plugins");
manifest.setVersion("1.0");
manifest.setDescription("TODO");

EffectManifestParameter* time = manifest.addParameter();
time->setId("time");
time->setName(QObject::tr("time"));
time->setDescription(QObject::tr("Controls the RT60 time of the reverb. "
"Actually controls the size of the plate. The mapping "
"between plate size and RT60 time is just a "
"heuristic, so it's not very accurate."));
time->setControlHint(EffectManifestParameter::CONTROL_KNOB_LINEAR);
time->setValueHint(EffectManifestParameter::VALUE_FLOAT);
time->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN);
time->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN);
time->setMinimum(0.1);
time->setDefault(1.0);
time->setMaximum(8.5);

EffectManifestParameter* damping = manifest.addParameter();
damping->setId("damping");
damping->setName(QObject::tr("damping"));
damping->setDescription(QObject::tr("Controls the degree that the surface "
"of the plate is damped."));
damping->setControlHint(EffectManifestParameter::CONTROL_KNOB_LINEAR);
damping->setValueHint(EffectManifestParameter::VALUE_FLOAT);
damping->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN);
damping->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN);
damping->setMinimum(0.0);
damping->setDefault(0.5);
damping->setMaximum(1.0);

return manifest;
}

ReverbEffect::ReverbEffect(EngineEffect* pEffect,
const EffectManifest& manifest)
: m_pTimeParameter(pEffect->getParameterById("time")),
m_pDampingParameter(pEffect->getParameterById("damping")) {
}

ReverbEffect::~ReverbEffect() {
for (QMap<QString, GroupState*>::iterator it = m_groupState.begin();
it != m_groupState.end();) {
for (int i = 0; i < 8; i++) {
waveguide_nl_reset((*it)->waveguide[i]);
}
SampleUtil::free((*it)->out);
delete it.value();
it = m_groupState.erase(it);
}
qDebug() << debugString() << "destroyed";
}

void ReverbEffect::process(const QString& group,
const CSAMPLE* pInput, CSAMPLE* pOutput,
const unsigned int numSamples) {
GroupState* pGroupState = m_groupState.value(group, NULL);
if (pGroupState == NULL) {
pGroupState = new GroupState();
m_groupState[group] = pGroupState;
}
GroupState& group_state = *pGroupState;

CSAMPLE time = m_pTimeParameter ?
m_pTimeParameter->value().toDouble() : 1.0f;
CSAMPLE damping = m_pDampingParameter ?
m_pDampingParameter->value().toDouble() : 0.5f;

// TODO(owilliams) check ranges?

unsigned long pos;
const float scale = powf(time * 0.117647f, 1.34f);
const float lpscale = 1.0f - damping * 0.93;

for (pos=0; pos<8; pos++) {
waveguide_nl_set_delay(group_state.waveguide[pos],
group_state.waveguide[pos]->size * scale);
}
for (pos=0; pos<4; pos++) {
waveguide_nl_set_fc(group_state.waveguide[pos], LP_INNER * lpscale);
}
for (; pos<8; pos++) {
waveguide_nl_set_fc(group_state.waveguide[pos], LP_OUTER * lpscale);
}

for (pos = 0; pos < numSamples - 1; pos+=2) {
const float alpha =
(group_state.out[0] + group_state.out[2] +
group_state.out[4] + group_state.out[6]) * 0.5f + pInput[pos];
const float beta =
(group_state.out[1] + group_state.out[9] + group_state.out[14])
* 0.666666666f;
const float gamma =
(group_state.out[3] + group_state.out[8] + group_state.out[11])
* 0.666666666f;
const float delta =
(group_state.out[5] + group_state.out[10] + group_state.out[13])
* 0.666666666f;
const float epsilon =
(group_state.out[7] + group_state.out[12] + group_state.out[15])
* 0.666666666f;

RUN_WG(0, beta, alpha);
RUN_WG(1, gamma, alpha);
RUN_WG(2, delta, alpha);
RUN_WG(3, epsilon, alpha);
RUN_WG(4, beta, gamma);
RUN_WG(5, gamma, delta);
RUN_WG(6, delta, epsilon);
RUN_WG(7, epsilon, beta);

pOutput[pos] = beta + pInput[pos];
pOutput[pos + 1] = beta + pInput[pos + 1];
}
}
64 changes: 64 additions & 0 deletions src/effects/native/reverbeffect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Ported from SWH Plate Reverb 1423.
// This effect is GPL code.

#ifndef REVERBEFFECT_H
#define REVERBEFFECT_H

#include <QMap>

#include "defs.h"
#include "util.h"
#include "engine/effects/engineeffect.h"
#include "engine/effects/engineeffectparameter.h"
#include "effects/effectprocessor.h"
#include "effects/native/waveguide_nl.h"
#include "sampleutil.h"

#define LP_INNER 0.96f
#define LP_OUTER 0.983f
const unsigned int kOutBufSize = 32;

class ReverbEffect : public EffectProcessor {
public:
ReverbEffect(EngineEffect* pEffect, const EffectManifest& manifest);
virtual ~ReverbEffect();

static QString getId();
static EffectManifest getManifest();

// See effectprocessor.h
void process(const QString& group,
const CSAMPLE* pInput, CSAMPLE* pOutput,
const unsigned int numSamples);

private:
QString debugString() const {
return getId();
}

EngineEffectParameter* m_pTimeParameter;
EngineEffectParameter* m_pDampingParameter;

struct GroupState {
GroupState() {
waveguide = (waveguide_nl**)malloc(8 * sizeof(waveguide_nl *));
waveguide[0] = waveguide_nl_new(2389, LP_INNER, 0.04f, 0.0f);
waveguide[1] = waveguide_nl_new(4742, LP_INNER, 0.17f, 0.0f);
waveguide[2] = waveguide_nl_new(4623, LP_INNER, 0.52f, 0.0f);
waveguide[3] = waveguide_nl_new(2142, LP_INNER, 0.48f, 0.0f);
waveguide[4] = waveguide_nl_new(5597, LP_OUTER, 0.32f, 0.0f);
waveguide[5] = waveguide_nl_new(3692, LP_OUTER, 0.89f, 0.0f);
waveguide[6] = waveguide_nl_new(5611, LP_OUTER, 0.28f, 0.0f);
waveguide[7] = waveguide_nl_new(3703, LP_OUTER, 0.29f, 0.0f);

out = SampleUtil::alloc(kOutBufSize);
}
waveguide_nl** waveguide;
CSAMPLE* out;
};
QMap<QString, GroupState*> m_groupState;

DISALLOW_COPY_AND_ASSIGN(ReverbEffect);
};

#endif /* REVERBEFFECT_H */
Loading

0 comments on commit 8ec6a42

Please sign in to comment.