Compare commits
No commits in common. "995339d2a212c2bd6c235fa7390cc3feab49060d" and "2cf6beaf9d8250a54aba1966506ca404d5a8b038" have entirely different histories.
995339d2a2
...
2cf6beaf9d
@ -1,3 +1,3 @@
|
|||||||
# https://geraintluff.github.io/SUPPORT.txt/
|
# https://geraintluff.github.io/SUPPORT.txt/
|
||||||
|
|
||||||
2026-06-01 Geraint Luff <geraint@signalsmith-audio.co.uk>
|
2026-01-01 Geraint Luff <geraint@signalsmith-audio.co.uk>
|
||||||
|
|||||||
@ -31,7 +31,7 @@ struct AnalyserSTFX : public BaseEffect {
|
|||||||
|
|
||||||
template<class Storage>
|
template<class Storage>
|
||||||
void state(Storage &storage) {
|
void state(Storage &storage) {
|
||||||
storage.info("Analyser", "A Bark-scale spectrum analyser");
|
storage.info("[Basics] Analyser", "A Bark-scale spectrum analyser");
|
||||||
storage.version(0);
|
storage.version(0);
|
||||||
|
|
||||||
storage.range("barkResolution", barkResolution)
|
storage.range("barkResolution", barkResolution)
|
||||||
|
|||||||
76
chorus.h
76
chorus.h
@ -40,11 +40,10 @@ struct ChorusSTFX : public BaseEffect {
|
|||||||
ParamRange mix{0.6};
|
ParamRange mix{0.6};
|
||||||
ParamRange depthMs{15};
|
ParamRange depthMs{15};
|
||||||
ParamRange detune{5};
|
ParamRange detune{5};
|
||||||
ParamRange stereo{1};
|
|
||||||
|
|
||||||
template<class Storage>
|
template<class Storage>
|
||||||
void state(Storage &storage) {
|
void state(Storage &storage) {
|
||||||
storage.info("Chorus", "");
|
storage.info("[Basics] Chorus", "");
|
||||||
storage.version(0);
|
storage.version(0);
|
||||||
|
|
||||||
stfx::units::rangePercent(storage.range("mix", mix)
|
stfx::units::rangePercent(storage.range("mix", mix)
|
||||||
@ -60,10 +59,6 @@ struct ChorusSTFX : public BaseEffect {
|
|||||||
.info("detune", "detuning depth")
|
.info("detune", "detuning depth")
|
||||||
.range(1, 8, 50)
|
.range(1, 8, 50)
|
||||||
.unit(" s.t.", 0);
|
.unit(" s.t.", 0);
|
||||||
|
|
||||||
stfx::units::rangePercent(storage.range("stereo", stereo)
|
|
||||||
.info("stereo", "")
|
|
||||||
.range(0, 0.5, 1.5));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
@ -79,16 +74,12 @@ struct ChorusSTFX : public BaseEffect {
|
|||||||
void reset() {
|
void reset() {
|
||||||
delay.reset();
|
delay.reset();
|
||||||
phase = 0;
|
phase = 0;
|
||||||
stereoPhase = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Io, class Config, class Block>
|
template<class Io, class Config, class Block>
|
||||||
void processSTFX(Io &io, Config &config, Block &block) {
|
void processSTFX(Io &io, Config &config, Block &block) {
|
||||||
Sample detuneHz = detune*0.45f/depthMs; // 0.45ms oscillation at 1Hz is about 1 semitone
|
Sample detuneHz = detune*0.45f/depthMs; // 0.45ms oscillation at 1Hz is about 1 semitone
|
||||||
Sample phaseStep = detuneHz/config.sampleRate;
|
Sample phaseStep = detuneHz/config.sampleRate;
|
||||||
Sample stereoPhaseStep = phaseStep/Sample(M_PI);
|
|
||||||
Complex stereoComplexPerOutput = std::polar(Sample(1), Sample(2*M_PI)/config.outputChannels);
|
|
||||||
Complex stereoComplexPerInternal = std::polar(Sample(1), Sample(1));
|
|
||||||
|
|
||||||
bool fading = depthMs.from() != depthMs.to();
|
bool fading = depthMs.from() != depthMs.to();
|
||||||
Sample depthSamples = depthMs.to()*0.001*config.sampleRate;
|
Sample depthSamples = depthMs.to()*0.001*config.sampleRate;
|
||||||
@ -97,16 +88,8 @@ struct ChorusSTFX : public BaseEffect {
|
|||||||
std::array<Sample, 6> multiIn, multiOut;
|
std::array<Sample, 6> multiIn, multiOut;
|
||||||
std::array<Sample, 6> delaySamples;
|
std::array<Sample, 6> delaySamples;
|
||||||
|
|
||||||
auto wetDryFn = [](double mix, double width, bool isWet){
|
auto dry = block.smooth(mix, [](double m){return 1 - m*m;});
|
||||||
// Equal-power wet/dry fade
|
auto wet = block.smooth(mix, [](double m){return m*(2 - m)/std::sqrt(Sample(6));});
|
||||||
double m = isWet ? std::sin(M_PI*mix/2) : std::cos(M_PI*mix/2);
|
|
||||||
// Compensate for extra energy from stereo
|
|
||||||
return m/std::sqrt(6*(1 + width*width/2));
|
|
||||||
};
|
|
||||||
auto dry = block.smooth(wetDryFn(mix.from(), stereo.from(), false), wetDryFn(mix.to(), stereo.to(), false));
|
|
||||||
auto wet = block.smooth(wetDryFn(mix.from(), stereo.from(), true), wetDryFn(mix.to(), stereo.to(), true));
|
|
||||||
bool notMono = (config.outputChannels > 1);
|
|
||||||
auto width = block.smooth(stereo);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < block.length; ++i) {
|
for (size_t i = 0; i < block.length; ++i) {
|
||||||
for (size_t c = 0; c < 6; ++c) {
|
for (size_t c = 0; c < 6; ++c) {
|
||||||
@ -115,50 +98,34 @@ struct ChorusSTFX : public BaseEffect {
|
|||||||
}
|
}
|
||||||
delay.write(multiIn);
|
delay.write(multiIn);
|
||||||
|
|
||||||
Complex phaseComplex = std::polar(Sample(1), phase*Sample(2*M_PI));
|
for (size_t oc = 0; oc < config.outputChannels; ++oc) {
|
||||||
for (size_t c = 0; c < 6; ++c) {
|
Complex phaseComplex = std::polar(Sample(1), phase*Sample(2*M_PI) + oc*Sample(2.632));
|
||||||
Sample osc = (phaseComplex*oscillatorOffsets[c]).real();
|
|
||||||
delaySamples[c] = depthSamples*Sample(0.5)*(1 + osc);
|
|
||||||
}
|
|
||||||
delay.readMulti(delaySamples, multiOut);
|
|
||||||
|
|
||||||
if (fading) {
|
|
||||||
// read a second set of delay times, and fade between them
|
|
||||||
std::array<Sample, 6> multiOutFrom;
|
|
||||||
for (size_t c = 0; c < 6; ++c) {
|
for (size_t c = 0; c < 6; ++c) {
|
||||||
Sample osc = (phaseComplex*oscillatorOffsets[c]).real();
|
Sample osc = (phaseComplex*oscillatorOffsets[c]).real();
|
||||||
delaySamples[c] = depthSamplesFrom*Sample(0.5)*(1 + osc);
|
delaySamples[c] = depthSamples*Sample(0.5)*(1 + osc);
|
||||||
}
|
}
|
||||||
delay.readMulti(delaySamples, multiOutFrom);
|
delay.readMulti(delaySamples, multiOut);
|
||||||
for (size_t c = 0; c < 6; ++c) {
|
|
||||||
multiOut[c] = block.fade(i, multiOutFrom[c], multiOut[c]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arbitrarily chosen gains, 4 positive, 2 negative
|
if (fading) {
|
||||||
Sample sum = multiOut[0] - multiOut[1] + multiOut[2] + multiOut[3] - multiOut[4] + multiOut[5];
|
// read a second set of delay times, and fade between them
|
||||||
if (notMono) {
|
std::array<Sample, 6> multiOutFrom;
|
||||||
Complex stereoComplex = std::polar(Sample(width.at(i)), stereoPhase*Sample(2*M_PI));
|
for (size_t c = 0; c < 6; ++c) {
|
||||||
for (size_t oc = 0; oc < config.outputChannels; ++oc) {
|
Sample osc = (phaseComplex*oscillatorOffsets[c]).real();
|
||||||
Sample sumC = sum;
|
delaySamples[c] = depthSamplesFrom*Sample(0.5)*(1 + osc);
|
||||||
Complex rot = stereoComplex;
|
}
|
||||||
for (size_t i = 0; i < 6; ++i) {
|
delay.readMulti(delaySamples, multiOutFrom);
|
||||||
sumC += rot.real()*multiOut[i];
|
for (size_t c = 0; c < 6; ++c) {
|
||||||
rot *= stereoComplexPerInternal;
|
multiOut[c] = block.fade(i, multiOutFrom[c], multiOut[c]);
|
||||||
}
|
}
|
||||||
|
|
||||||
io.output[oc][i] = multiIn[oc]*dry.at(i) + sumC*wet.at(i);
|
|
||||||
stereoComplex *= stereoComplexPerOutput;
|
|
||||||
}
|
}
|
||||||
stereoPhase += stereoPhaseStep;
|
|
||||||
} else {
|
|
||||||
io.output[0][i] = multiIn[0]*dry.at(i) + sum*wet.at(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Arbitrarily chosen gains, 4 positive, 2 negative
|
||||||
|
Sample sum = multiOut[0] - multiOut[1] + multiOut[2] + multiOut[3] - multiOut[4] + multiOut[5];
|
||||||
|
io.output[oc][i] = multiIn[oc]*dry.at(i) + sum*wet.at(i);
|
||||||
|
}
|
||||||
phase += phaseStep;
|
phase += phaseStep;
|
||||||
}
|
}
|
||||||
phase -= std::floor(phase);
|
phase -= std::floor(phase);
|
||||||
stereoPhase -= std::floor(stereoPhase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Storage>
|
template<class Storage>
|
||||||
@ -169,7 +136,6 @@ private:
|
|||||||
|
|
||||||
signalsmith::delay::MultiDelay<Sample, signalsmith::delay::InterpolatorLagrange7> delay;
|
signalsmith::delay::MultiDelay<Sample, signalsmith::delay::InterpolatorLagrange7> delay;
|
||||||
Sample phase = 0;
|
Sample phase = 0;
|
||||||
Sample stereoPhase = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
#include "signalsmith-basics/analyser.h"
|
#include "signalsmith-basics/analyser.h"
|
||||||
#include "signalsmith-basics/chorus.h"
|
#include "signalsmith-basics/chorus.h"
|
||||||
#include "signalsmith-basics/crunch.h"
|
#include "signalsmith-basics/crunch.h"
|
||||||
#include "signalsmith-basics/freq-shifter.h"
|
|
||||||
#include "signalsmith-basics/limiter.h"
|
#include "signalsmith-basics/limiter.h"
|
||||||
#include "signalsmith-basics/reverb.h"
|
#include "signalsmith-basics/reverb.h"
|
||||||
|
|
||||||
@ -26,7 +25,7 @@ bool clap_init(const char *path) {
|
|||||||
}, {
|
}, {
|
||||||
CLAP_PLUGIN_FEATURE_ANALYZER,
|
CLAP_PLUGIN_FEATURE_ANALYZER,
|
||||||
});
|
});
|
||||||
|
|
||||||
plugins.add<signalsmith::basics::ChorusSTFX>({
|
plugins.add<signalsmith::basics::ChorusSTFX>({
|
||||||
.clap_version = CLAP_VERSION,
|
.clap_version = CLAP_VERSION,
|
||||||
.id = "uk.co.signalsmith.basics.chorus",
|
.id = "uk.co.signalsmith.basics.chorus",
|
||||||
@ -55,20 +54,6 @@ bool clap_init(const char *path) {
|
|||||||
CLAP_PLUGIN_FEATURE_DISTORTION,
|
CLAP_PLUGIN_FEATURE_DISTORTION,
|
||||||
});
|
});
|
||||||
|
|
||||||
plugins.add<signalsmith::basics::FreqShifterSTFX>({
|
|
||||||
.clap_version = CLAP_VERSION,
|
|
||||||
.id = "uk.co.signalsmith.basics.freq-shifter",
|
|
||||||
.name = "[Basics] Frequency Shifter",
|
|
||||||
.vendor = "Signalsmith Audio",
|
|
||||||
.url = "",
|
|
||||||
.manual_url = "",
|
|
||||||
.support_url = "",
|
|
||||||
.version = "1.0.0"
|
|
||||||
}, {
|
|
||||||
CLAP_PLUGIN_FEATURE_AUDIO_EFFECT,
|
|
||||||
CLAP_PLUGIN_FEATURE_FREQUENCY_SHIFTER,
|
|
||||||
});
|
|
||||||
|
|
||||||
plugins.add<signalsmith::basics::LimiterSTFX>({
|
plugins.add<signalsmith::basics::LimiterSTFX>({
|
||||||
.clap_version = CLAP_VERSION,
|
.clap_version = CLAP_VERSION,
|
||||||
.id = "uk.co.signalsmith.basics.limiter",
|
.id = "uk.co.signalsmith.basics.limiter",
|
||||||
|
|||||||
7
crunch.h
7
crunch.h
@ -1,6 +1,7 @@
|
|||||||
/* Copyright 2022 Signalsmith Audio Ltd. / Geraint Luff
|
/* Copyright 2022 Signalsmith Audio Ltd. / Geraint Luff
|
||||||
See LICENSE.txt and SUPPORT.txt */
|
See LICENSE.txt and SUPPORT.txt */
|
||||||
#pragma once
|
#ifndef SIGNALSMITH_BASICS_CRUNCH_H
|
||||||
|
#define SIGNALSMITH_BASICS_CRUNCH_H
|
||||||
|
|
||||||
#include "dsp/rates.h"
|
#include "dsp/rates.h"
|
||||||
#include "dsp/filters.h"
|
#include "dsp/filters.h"
|
||||||
@ -39,7 +40,7 @@ struct CrunchSTFX : public BaseEffect {
|
|||||||
|
|
||||||
template<class Storage>
|
template<class Storage>
|
||||||
void state(Storage &storage) {
|
void state(Storage &storage) {
|
||||||
storage.info("Crunch", "A simple distortion/saturation");
|
storage.info("[Basics] Crunch", "A simple distortion/saturation");
|
||||||
int version = storage.version(0);
|
int version = storage.version(0);
|
||||||
if (version != 0) return;
|
if (version != 0) return;
|
||||||
|
|
||||||
@ -177,3 +178,5 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
|
||||||
|
#endif // include guard
|
||||||
|
|||||||
118
freq-shifter.h
118
freq-shifter.h
@ -1,118 +0,0 @@
|
|||||||
/* Copyright 2022 Signalsmith Audio Ltd. / Geraint Luff
|
|
||||||
See LICENSE.txt and SUPPORT.txt */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "modules/hilbert-iir/hilbert.h"
|
|
||||||
|
|
||||||
#include "stfx/stfx-library.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
namespace signalsmith { namespace basics {
|
|
||||||
|
|
||||||
template<class BaseEffect>
|
|
||||||
class FreqShifterSTFX;
|
|
||||||
|
|
||||||
using FreqShifterFloat = stfx::LibraryEffect<float, FreqShifterSTFX>;
|
|
||||||
using FreqShifterDouble = stfx::LibraryEffect<double, FreqShifterSTFX>;
|
|
||||||
|
|
||||||
template<class BaseEffect>
|
|
||||||
struct FreqShifterSTFX : public BaseEffect {
|
|
||||||
using typename BaseEffect::Sample;
|
|
||||||
using Complex = std::complex<Sample>;
|
|
||||||
using typename BaseEffect::ParamRange;
|
|
||||||
using typename BaseEffect::ParamStepped;
|
|
||||||
|
|
||||||
static double unitToShiftHz(double x) {
|
|
||||||
return 100*x/(1.1 - x*x);
|
|
||||||
}
|
|
||||||
static double shiftHzToUnit(double y) {
|
|
||||||
return (std::sqrt(2500 + 1.1*y*y) - 50)/y;
|
|
||||||
}
|
|
||||||
|
|
||||||
ParamRange mix{1};
|
|
||||||
ParamRange shift{shiftHzToUnit(50)};
|
|
||||||
ParamStepped reflect{0};
|
|
||||||
|
|
||||||
template<class Storage>
|
|
||||||
void state(Storage &storage) {
|
|
||||||
storage.info("Frequency Shifter", "A Hilbert / Bode single-sideband modulator");
|
|
||||||
int version = storage.version(0);
|
|
||||||
if (version != 0) return;
|
|
||||||
|
|
||||||
stfx::units::rangePercent(storage.range("mix", mix)
|
|
||||||
.info("mix", "wet/dry")
|
|
||||||
.range(0, 0.5, 1));
|
|
||||||
storage.range("shift", shift)
|
|
||||||
.info("shift", "pre-distortion input gain")
|
|
||||||
.range(-1, 0, 1)
|
|
||||||
.unit("Hz", 1, shiftHzToUnit, unitToShiftHz, shiftHzToUnit(-9.99), shiftHzToUnit(9.99))
|
|
||||||
.unit("Hz", 0, shiftHzToUnit, unitToShiftHz);
|
|
||||||
storage.stepped("reflect", reflect)
|
|
||||||
.info("reflect", "0Hz reflection mode")
|
|
||||||
.range(0, 3)
|
|
||||||
.label(0, "no reflect/duplicate", "reflect below 0Hz", "duplicate above 0Hz", "always reflect/duplicate");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Config>
|
|
||||||
void configureSTFX(Config &config) {
|
|
||||||
auto channels = config.outputChannels = config.inputChannels;
|
|
||||||
config.auxInputs.resize(0);
|
|
||||||
config.auxOutputs.resize(0);
|
|
||||||
|
|
||||||
hilbert = {Sample(config.sampleRate), channels};
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
shiftPhaseBefore = shiftPhaseAfter = 0;
|
|
||||||
shiftBefore = shiftAfter = 1;
|
|
||||||
hilbert.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <class Io, class Config, class Block>
|
|
||||||
void processSTFX(Io &io, Config &config, Block &block) {
|
|
||||||
auto dry = block.smooth(mix, [](double m){return 1 - m*m;});
|
|
||||||
auto wet = block.smooth(mix, [](double m){return m*(2 - m);});
|
|
||||||
|
|
||||||
auto phaseStep = block.smooth(shift, [&](double u){
|
|
||||||
auto hz = unitToShiftHz(u);
|
|
||||||
return hz/config.sampleRate;
|
|
||||||
});
|
|
||||||
|
|
||||||
int mode = reflect;
|
|
||||||
bool noReflectDown = (mode == 0 || mode == 2);
|
|
||||||
bool duplicateUp = (mode == 2 || mode == 3);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < block.length; ++i) {
|
|
||||||
auto gainDry = dry.at(i), gainWet = wet.at(i);
|
|
||||||
for (size_t c = 0; c < config.inputChannels; ++c) {
|
|
||||||
Sample x = io.input[c][i];
|
|
||||||
// In general, this may alias at the high end when shifting up
|
|
||||||
// but our Hilbert has a 20kHz lowpass, so that's enough
|
|
||||||
// room for our maximum +1000Hz shift
|
|
||||||
Complex y = shiftAfter*hilbert(x*shiftBefore, c);
|
|
||||||
io.output[c][i] = gainDry*x + gainWet*y.real();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ps = phaseStep.at(i);
|
|
||||||
bool shiftInput = (ps < 0) ? noReflectDown : duplicateUp;
|
|
||||||
if (shiftInput) {
|
|
||||||
shiftPhaseBefore += ps;
|
|
||||||
shiftBefore = std::polar(Sample(1), shiftPhaseBefore*Sample(2*M_PI));
|
|
||||||
} else {
|
|
||||||
shiftPhaseAfter += ps;
|
|
||||||
shiftAfter = std::polar(Sample(1), shiftPhaseAfter*Sample(2*M_PI));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shiftPhaseBefore -= std::floor(shiftPhaseBefore);
|
|
||||||
shiftPhaseAfter -= std::floor(shiftPhaseAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Sample shiftPhaseBefore = 0, shiftPhaseAfter = 0;
|
|
||||||
Complex shiftBefore = 1, shiftAfter = 1;
|
|
||||||
signalsmith::hilbert::HilbertIIR<Sample> hilbert;
|
|
||||||
};
|
|
||||||
|
|
||||||
}} // namespace
|
|
||||||
@ -1 +0,0 @@
|
|||||||
#include "../../freq-shifter.h"
|
|
||||||
@ -1,6 +1,7 @@
|
|||||||
/* Copyright 2022 Signalsmith Audio Ltd. / Geraint Luff
|
/* Copyright 2022 Signalsmith Audio Ltd. / Geraint Luff
|
||||||
Released under the Boost Software License (see LICENSE.txt) */
|
Released under the Boost Software License (see LICENSE.txt) */
|
||||||
#pragma once
|
#ifndef SIGNALSMITH_BASICS_LIMITER_H
|
||||||
|
#define SIGNALSMITH_BASICS_LIMITER_H
|
||||||
|
|
||||||
#include "dsp/delay.h"
|
#include "dsp/delay.h"
|
||||||
#include "dsp/envelopes.h"
|
#include "dsp/envelopes.h"
|
||||||
@ -35,7 +36,7 @@ struct LimiterSTFX : public BaseEffect {
|
|||||||
|
|
||||||
template<class Storage>
|
template<class Storage>
|
||||||
void state(Storage &storage) {
|
void state(Storage &storage) {
|
||||||
storage.info("Limiter", "A simple lookahead limiter");
|
storage.info("[Basics] Limiter", "A simple lookahead limiter");
|
||||||
int version = storage.version(4);
|
int version = storage.version(4);
|
||||||
if (version != 4) return;
|
if (version != 4) return;
|
||||||
|
|
||||||
@ -201,3 +202,5 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
|
||||||
|
#endif // include guard
|
||||||
|
|||||||
6
reverb.h
6
reverb.h
@ -1,6 +1,7 @@
|
|||||||
/* Copyright 2022 Signalsmith Audio Ltd. / Geraint Luff
|
/* Copyright 2022 Signalsmith Audio Ltd. / Geraint Luff
|
||||||
Released under the Boost Software License (see LICENSE.txt) */
|
Released under the Boost Software License (see LICENSE.txt) */
|
||||||
#pragma once
|
#ifndef SIGNALSMITH_BASICS_REVERB_H
|
||||||
|
#define SIGNALSMITH_BASICS_REVERB_H
|
||||||
|
|
||||||
#include "dsp/delay.h"
|
#include "dsp/delay.h"
|
||||||
#include "dsp/mix.h"
|
#include "dsp/mix.h"
|
||||||
@ -42,7 +43,7 @@ struct ReverbSTFX : public BaseEffect {
|
|||||||
template<class Storage>
|
template<class Storage>
|
||||||
void state(Storage &storage) {
|
void state(Storage &storage) {
|
||||||
|
|
||||||
storage.info("Reverb", "An FDN reverb");
|
storage.info("[Basics] Reverb", "An FDN reverb");
|
||||||
int version = storage.version(5);
|
int version = storage.version(5);
|
||||||
if (version != 5) return;
|
if (version != 5) return;
|
||||||
|
|
||||||
@ -407,3 +408,4 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
#endif // include guard
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user