summaryrefslogtreecommitdiffstats
path: root/src/effects/backends/builtin/glitcheffect.cpp
blob: 29ca12835bc6d4f7238709e1aac72e83b79de228 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include "effects/backends/builtin/glitcheffect.h"

#include <QtDebug>

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

QString GlitchEffect::getId() {
    return "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, 2.0);

    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;
    }

    int delay_samples = delay_frames * engineParameters.channelCount();
    pGroupState->sample_count += engineParameters.samplesPerBuffer();
    if (pGroupState->sample_count >= delay_samples) {
        if (m_pDelayParameter->value() < 2.0) {
            SampleUtil::copy(pGroupState->repeat_buf.data(),
                    pInput,
                    engineParameters.samplesPerBuffer());
        }
        pGroupState->sample_count = 0;
    }

    SampleUtil::copy(
            pOutput,
            pGroupState->repeat_buf.data(),
            engineParameters.samplesPerBuffer());
}