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 Disintegrator effect to LMMS #5161

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7f1c954
Merge pull request #8 from LMMS/master
LostRobotMusic Jun 21, 2019
0bb50a8
Merge pull request #11 from LMMS/master
LostRobotMusic Aug 7, 2019
a332d47
Merge pull request #12 from LMMS/master
LostRobotMusic Aug 30, 2019
e83d44a
Add Disintegrator effect
LostRobotMusic Aug 30, 2019
a630f32
Add Disintegrator effect
LostRobotMusic Aug 30, 2019
2fce224
Added comments, removed unnecessary detuneWithOctaves function.
LostRobotMusic Aug 30, 2019
6a44a51
Resolve code/style reviews
LostRobotMusic Aug 30, 2019
5a4670e
Changed filters to use BasicFilters.h, fixed crashing bug (caused by …
LostRobotMusic Aug 30, 2019
328c765
Increase buffer size to correct amount
LostRobotMusic Aug 30, 2019
6b09fce
Add self-modulation mode
LostRobotMusic Aug 30, 2019
4556151
Clip modulator at 0db for self-modulation mode
LostRobotMusic Aug 30, 2019
02b9a2e
Minor CPU boost I missed
LostRobotMusic Sep 4, 2019
ee442e8
Merge branch 'master' into disintegrator
LostRobotMusic Nov 24, 2019
d2cb54f
Hippity hoppity squash and merge hides this probably
LostRobotMusic Nov 24, 2019
06024b8
Fix knob ranges
LostRobotMusic Nov 24, 2019
89f0501
Resolve code/style reviews
LostRobotMusic Apr 18, 2020
624b2f2
Put some duplicate code into for loops
LostRobotMusic Apr 18, 2020
7e231b8
Remove newline in untouched file
LostRobotMusic Apr 18, 2020
ee0420a
Make effect work correctly with sample rate changes
LostRobotMusic Apr 19, 2020
fa0f436
Apply suggestions from code review
PhysSong Dec 10, 2020
1235e41
Fix stuff
LostRobotMusic Dec 27, 2020
5665902
Fix Amount knob units
LostRobotMusic Dec 27, 2020
5384c5d
Fix more stuff
LostRobotMusic Dec 28, 2020
88ea50d
Optimize
LostRobotMusic Dec 28, 2020
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
1 change: 1 addition & 0 deletions cmake/modules/PluginList.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ SET(LMMS_PLUGIN_LIST
carlarack
CrossoverEQ
Delay
Disintegrator
DualFilter
dynamics_processor
Eq
Expand Down
3 changes: 3 additions & 0 deletions plugins/Disintegrator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
INCLUDE(BuildPlugin)

BUILD_PLUGIN(disintegrator Disintegrator.cpp DisintegratorControls.cpp DisintegratorControlDialog.cpp MOCFILES DisintegratorControls.h DisintegratorControlDialog.h EMBEDDED_RESOURCES artwork.png logo.png)
259 changes: 259 additions & 0 deletions plugins/Disintegrator/Disintegrator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
/*
* Disintegrator.cpp
*
* Copyright (c) 2019 Lost Robot <r94231@gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/

#include "Disintegrator.h"

#include "embed.h"
#include "lmms_math.h"
#include "plugin_export.h"

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT disintegrator_plugin_descriptor =
{
STRINGIFY(PLUGIN_NAME),
"Disintegrator",
QT_TRANSLATE_NOOP("pluginBrowser", "A delay modulation effect for very aggressive digital distortion."),
"Lost Robot <r94231@gmail.com>",
0x0100,
Plugin::Effect,
new PluginPixmapLoader("logo"),
NULL,
NULL
} ;

}



DisintegratorEffect::DisintegratorEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key) :
Effect(&disintegrator_plugin_descriptor, parent, key),
m_disintegratorControls(this),
m_lp(Engine::mixer()->processingSampleRate()),
m_hp(Engine::mixer()->processingSampleRate()),
m_needsUpdate(true)
{
emit sampleRateChanged();
LostRobotMusic marked this conversation as resolved.
Show resolved Hide resolved
}



void DisintegratorEffect::sampleRateChanged()
{
m_sampleRate = Engine::mixer()->processingSampleRate();
m_sampleRateMult = m_sampleRate / 44100.f;
m_lp.setSampleRate(m_sampleRate);
m_hp.setSampleRate(m_sampleRate);
m_needsUpdate = true;

m_bufferSize = (m_sampleRate / 44100.f) * 200.f + 1.f;
for (int i = 0; i < 2; ++i)
{
m_inBuf[i].resize(m_bufferSize);
}
}



bool DisintegratorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
{
if (!isEnabled() || !isRunning ())
{
return false;
}

double outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();

const ValueBuffer * amountBuf = m_disintegratorControls.m_amountModel.valueBuffer();
const ValueBuffer * typeBuf = m_disintegratorControls.m_typeModel.valueBuffer();
const ValueBuffer * freqBuf = m_disintegratorControls.m_lowCutModel.valueBuffer();

// Update filters
if(m_needsUpdate || m_disintegratorControls.m_highCutModel.isValueChanged())
PhysSong marked this conversation as resolved.
Show resolved Hide resolved
{
m_lp.setLowpass(m_disintegratorControls.m_highCutModel.value());

PhysSong marked this conversation as resolved.
Show resolved Hide resolved
}
if(m_needsUpdate || m_disintegratorControls.m_lowCutModel.isValueChanged())
PhysSong marked this conversation as resolved.
Show resolved Hide resolved
{
m_hp.setHighpass(m_disintegratorControls.m_lowCutModel.value());
}
m_needsUpdate = false;

for (fpp_t f = 0; f < frames; ++f)
{
const float amount = amountBuf ? amountBuf->value(f) : m_disintegratorControls.m_amountModel.value();
const int type = typeBuf ? typeBuf->value(f) : m_disintegratorControls.m_typeModel.value();
const float freq = freqBuf ? freqBuf->value(f) : m_disintegratorControls.m_freqModel.value();

sample_t s[2] = {buf[f][0], buf[f][1]};

// Increment buffer read point
Copy link
Contributor

Choose a reason for hiding this comment

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

when implementing a circular buffer, it is common to have the buffer size a power of 2, with 512 being the next closest to the 500 you stare in the header.

The index increment and wrap at end of buffer can then be reduced to:

++m_inBufLoc;
m_inBufLoc &= (m_bufferSize - 1)

This is preferable as conditional branches can be processor expensive, and may also hinder some compiler side optimisations.

++m_inBufLoc;
if (m_inBufLoc >= m_bufferSize)
{
m_inBufLoc = 0;
}

// Write dry input to buffer
m_inBuf[0][m_inBufLoc] = s[0];
m_inBuf[1][m_inBufLoc] = s[1];

float newInBufLoc[2] = {0, 0};
float newInBufLocFrac[2] = {0, 0};

// Generate white noise or sine wave, apply filters, subtract the
Copy link
Contributor

Choose a reason for hiding this comment

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

might be preferable to just call it noise, not White Noise. White Noise has defined mathematical properties, that fast_rand() does not provide.

// result from the buffer read point and store in a variable.
switch (type)
{
case 0:// Mono Noise
{
newInBufLoc[0] = fast_rand() / (float)FAST_RAND_MAX;

newInBufLoc[0] = m_hp.update(newInBufLoc[0], 0);
newInBufLoc[0] = m_lp.update(newInBufLoc[0], 0);

newInBufLoc[0] = realfmod(m_inBufLoc - newInBufLoc[0] * amount * m_sampleRateMult, m_bufferSize);
newInBufLoc[1] = newInBufLoc[0];

// Distance between samples
newInBufLocFrac[0] = fmod(newInBufLoc[0], 1);
newInBufLocFrac[1] = newInBufLocFrac[0];

break;
}
case 1:// Stereo Noise
{
for (int i = 0; i < 2; ++i)
{
newInBufLoc[i] = fast_rand() / (float)FAST_RAND_MAX;

newInBufLoc[i] = m_hp.update(newInBufLoc[i], 0);
newInBufLoc[i] = m_lp.update(newInBufLoc[i], 0);

newInBufLoc[i] = realfmod(m_inBufLoc - newInBufLoc[i] * amount * m_sampleRateMult, m_bufferSize);

// Distance between samples
newInBufLocFrac[i] = fmod(newInBufLoc[i], 1);
}

break;
}
case 2:// Sine Wave
{
m_sineLoc = fmod(m_sineLoc + (freq / m_sampleRate * F_2PI), F_2PI);
LostRobotMusic marked this conversation as resolved.
Show resolved Hide resolved

newInBufLoc[0] = (sin(m_sineLoc) + 1) * 0.5f;

newInBufLoc[0] = realfmod(m_inBufLoc - newInBufLoc[0] * amount * m_sampleRateMult, m_bufferSize);
newInBufLoc[1] = newInBufLoc[0];

// Distance between samples
newInBufLocFrac[0] = fmod(newInBufLoc[0], 1);
newInBufLocFrac[1] = newInBufLocFrac[0];

break;
}
case 3:// Self-Modulation
{
for (int i = 0; i < 2; ++i)
{
newInBufLoc[i] = (qBound(-1.f, s[i], 1.f) + 1) * 0.5f;

newInBufLoc[i] = m_hp.update(newInBufLoc[i], 0);
newInBufLoc[i] = m_lp.update(newInBufLoc[i], 0);

newInBufLoc[i] = realfmod(m_inBufLoc - newInBufLoc[i] * amount * m_sampleRateMult, m_bufferSize);

// Distance between samples
newInBufLocFrac[i] = fmod(newInBufLoc[i], 1);
}

break;
}
}

for (int i = 0; i < 2; ++i)
{
if (newInBufLocFrac[i] == 0)
{
s[i] = m_inBuf[i][newInBufLoc[i]];
}
else
{
if (newInBufLoc[i] < m_bufferSize - 1)
{
s[i] = m_inBuf[i][floor(newInBufLoc[i])] * (1 - newInBufLocFrac[i]) + m_inBuf[i][ceil(newInBufLoc[i])] * newInBufLocFrac[i];
}
else// For when the interpolation wraps around to the beginning of the buffer
{
s[i] = m_inBuf[i][m_bufferSize - 1] * (1 - newInBufLocFrac[i]) + m_inBuf[i][0] * newInBufLocFrac[i];
Copy link
Contributor

Choose a reason for hiding this comment

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

see above comment

}
}
}

buf[f][0] = d * buf[f][0] + w * s[0];
buf[f][1] = d * buf[f][1] + w * s[1];

outSum += buf[f][0] + buf[f][1];
}

checkGate(outSum / frames);

return isRunning();
}



// Handles negative values properly, unlike fmod.
inline float DisintegratorEffect::realfmod(float k, float n)
{
float r = fmod(k, n);
return r < 0 ? r + n : r;
}



void DisintegratorEffect::clearFilterHistories()
{
m_lp.clearHistory();
m_hp.clearHistory();
}



extern "C"
{

// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main(Model* parent, void* data)
{
return new DisintegratorEffect(parent, static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(data));
}

}

73 changes: 73 additions & 0 deletions plugins/Disintegrator/Disintegrator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Disintegrator.h
*
* Copyright (c) 2019 Lost Robot <r94231@gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/


#ifndef DISINTEGRATOR_H
#define DISINTEGRATOR_H

#include "DisintegratorControls.h"

#include "BasicFilters.h"
#include "Effect.h"
#include "ValueBuffer.h"


class DisintegratorEffect : public Effect
{
public:
DisintegratorEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key);
bool processAudioBuffer(sampleFrame* buf, const fpp_t frames) override;

EffectControls* controls() override
{
return &m_disintegratorControls;
}

void sampleRateChanged();

inline float realfmod(float k, float n);
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the reason this function is declared inline?


void clearFilterHistories();

private:
DisintegratorControls m_disintegratorControls;

std::vector<float> m_inBuf[2];
int m_inBufLoc = 0;

float m_sineLoc = 0;

StereoLinkwitzRiley m_lp;
StereoLinkwitzRiley m_hp;
bool m_needsUpdate;

float m_sampleRate;
float m_sampleRateMult;
int m_bufferSize = 201;

friend class DisintegratorControls;

} ;

#endif
Loading