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

Glitch stutter built-in effect #11329

Merged
merged 8 commits into from
Jun 16, 2023
Merged
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/effects/backends/builtin/threebandbiquadeqeffect.cpp
src/effects/backends/builtin/tremoloeffect.cpp
src/effects/backends/builtin/whitenoiseeffect.cpp
src/effects/backends/builtin/glitcheffect.cpp
src/effects/backends/effectsbackendmanager.cpp
src/effects/chains/equalizereffectchain.cpp
src/effects/chains/outputeffectchain.cpp
Expand Down
2 changes: 2 additions & 0 deletions src/effects/backends/builtin/builtinbackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "effects/backends/builtin/autopaneffect.h"
#include "effects/backends/builtin/distortioneffect.h"
#include "effects/backends/builtin/echoeffect.h"
#include "effects/backends/builtin/glitcheffect.h"
#include "effects/backends/builtin/loudnesscontoureffect.h"
#include "effects/backends/builtin/metronomeeffect.h"
#include "effects/backends/builtin/phasereffect.h"
Expand Down Expand Up @@ -58,6 +59,7 @@ BuiltInBackend::BuiltInBackend() {
registerEffect<TremoloEffect>();
registerEffect<PitchShiftEffect>();
registerEffect<DistortionEffect>();
registerEffect<GlitchEffect>();
}

std::unique_ptr<EffectProcessor> BuiltInBackend::createProcessor(
Expand Down
123 changes: 123 additions & 0 deletions src/effects/backends/builtin/glitcheffect.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include "effects/backends/builtin/glitcheffect.h"

#include <QtDebug>

#include "util/math.h"
#include "util/sample.h"

QString GlitchEffect::getId() {
return QStringLiteral("org.mixxx.effects.glitch");
}

EffectManifestPointer GlitchEffect::getManifest() {
EffectManifestPointer pManifest(new EffectManifest());

pManifest->setAddDryToWet(false);
pManifest->setEffectRampsFromDry(false);

pManifest->setId(getId());
pManifest->setName(QObject::tr("Glitch"));
pManifest->setShortName(QObject::tr("Glitch"));
pManifest->setAuthor("The Mixxx Team");
pManifest->setVersion("1.0");
pManifest->setDescription(
QObject::tr("Periodically samples and repeats a small portion of "
"audio to create a glitchy metallic sound."));

EffectManifestParameterPointer delay = pManifest->addParameter();
delay->setId("delay_time");
delay->setName(QObject::tr("Time"));
delay->setShortName(QObject::tr("Time"));
delay->setDescription(QObject::tr(
"Delay time\n"
"1/8 - 2 beats if tempo is detected\n"
"1/8 - 2 seconds if no tempo is detected"));
delay->setValueScaler(EffectManifestParameter::ValueScaler::Linear);
delay->setUnitsHint(EffectManifestParameter::UnitsHint::Beats);
delay->setRange(0.0, 0.5, kMaxDelay);

EffectManifestParameterPointer quantize = pManifest->addParameter();
quantize->setId("quantize");
quantize->setName(QObject::tr("Quantize"));
quantize->setShortName(QObject::tr("Quantize"));
quantize->setDescription(QObject::tr(
"Round the Time parameter to the nearest 1/8 beat."));
quantize->setValueScaler(EffectManifestParameter::ValueScaler::Toggle);
quantize->setUnitsHint(EffectManifestParameter::UnitsHint::Unknown);
quantize->setRange(0, 1, 1);

EffectManifestParameterPointer triplet = pManifest->addParameter();
triplet->setId("triplet");
triplet->setName(QObject::tr("Triplets"));
triplet->setShortName(QObject::tr("Triplets"));
triplet->setDescription(
QObject::tr("When the Quantize parameter is enabled, divide "
"rounded 1/8 beats of Time parameter by 3."));
triplet->setValueScaler(EffectManifestParameter::ValueScaler::Toggle);
triplet->setUnitsHint(EffectManifestParameter::UnitsHint::Unknown);
triplet->setRange(0, 0, 1);

return pManifest;
}

void GlitchEffect::loadEngineEffectParameters(
const QMap<QString, EngineEffectParameterPointer>& parameters) {
m_pDelayParameter = parameters.value("delay_time");
m_pQuantizeParameter = parameters.value("quantize");
m_pTripletParameter = parameters.value("triplet");
}

void GlitchEffect::processChannel(
GlitchGroupState* pGroupState,
const CSAMPLE* pInput,
CSAMPLE* pOutput,
const mixxx::EngineParameters& engineParameters,
const EffectEnableState enableState,
const GroupFeatureState& groupFeatures) {
// The minimum of the parameter is zero so the exact center of the knob is 1 beat.
double period = m_pDelayParameter->value();

int delay_frames;
double min_delay;
if (groupFeatures.has_beat_length_sec) {
if (m_pQuantizeParameter->toBool()) {
period = roundToFraction(period, 8);
if (m_pTripletParameter->toBool()) {
period /= 3.0;
}
}
period = std::max(period, 1 / 8.0);
delay_frames = static_cast<int>(period * groupFeatures.beat_length_sec *
engineParameters.sampleRate());
min_delay = 1 / 8.0 * groupFeatures.beat_length_sec * engineParameters.sampleRate();
} else {
delay_frames = static_cast<int>(period * engineParameters.sampleRate());
min_delay = 1 / 8.0 * engineParameters.sampleRate();
}

if (delay_frames < min_delay) {
SampleUtil::copy(
pOutput,
pInput,
engineParameters.samplesPerBuffer());
return;
}

pcktm marked this conversation as resolved.
Show resolved Hide resolved
int delay_samples = delay_frames * engineParameters.channelCount();
pGroupState->sample_count += engineParameters.samplesPerBuffer();
if (pGroupState->sample_count >= delay_samples) {
// If the delay parameter is at its maximum value, we don't update the `repeat_buf`
// in order to achieve an "audio freeze" effect.
if (m_pDelayParameter->value() < kMaxDelay) {
SampleUtil::copy(pGroupState->repeat_buf.data(),
pInput,
engineParameters.samplesPerBuffer());
}
pGroupState->sample_count = 0;
}

SampleUtil::copy(
pOutput,
pGroupState->repeat_buf.data(),
engineParameters.samplesPerBuffer());
}
67 changes: 67 additions & 0 deletions src/effects/backends/builtin/glitcheffect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#include <QMap>

#include "effects/backends/effectprocessor.h"
#include "engine/effects/engineeffect.h"
#include "engine/effects/engineeffectparameter.h"
#include "engine/engine.h"
#include "util/class.h"
#include "util/defs.h"
#include "util/sample.h"
#include "util/samplebuffer.h"

namespace {
constexpr double kMaxDelay = 2.0;
}

class GlitchGroupState : public EffectState {
public:
GlitchGroupState(const mixxx::EngineParameters& engineParameters)
: EffectState(engineParameters) {
audioParametersChanged(engineParameters);
clear();
}

void audioParametersChanged(const mixxx::EngineParameters& engineParameters) {
repeat_buf = mixxx::SampleBuffer(engineParameters.samplesPerBuffer());
};

void clear() {
repeat_buf.clear();
sample_count = 0;
};

mixxx::SampleBuffer repeat_buf;
long long sample_count;
};

class GlitchEffect : public EffectProcessorImpl<GlitchGroupState> {
public:
GlitchEffect() = default;

static QString getId();
static EffectManifestPointer getManifest();

void loadEngineEffectParameters(
const QMap<QString, EngineEffectParameterPointer>& parameters) override;

void processChannel(
GlitchGroupState* pState,
const CSAMPLE* pInput,
CSAMPLE* pOutput,
const mixxx::EngineParameters& engineParameters,
const EffectEnableState enableState,
const GroupFeatureState& groupFeatures) override;

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

EngineEffectParameterPointer m_pDelayParameter;
EngineEffectParameterPointer m_pQuantizeParameter;
EngineEffectParameterPointer m_pTripletParameter;

DISALLOW_COPY_AND_ASSIGN(GlitchEffect);
};