Added a leak detector and corrected some bugs
This commit is contained in:
parent
99c3e8878f
commit
dba851385d
16 changed files with 181 additions and 61 deletions
|
|
@ -181,7 +181,8 @@ void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
originalSpan.remove_prefix(releaseDelay);
|
originalSpan.remove_prefix(releaseDelay);
|
||||||
currentValue = originalSpan.front();
|
if (originalSpan.size() > 0)
|
||||||
|
currentValue = originalSpan.front();
|
||||||
step = std::exp((std::log(config::virtuallyZero) - std::log(currentValue)) / (release > 0 ? release : 1));
|
step = std::exp((std::log(config::virtuallyZero) - std::log(currentValue)) / (release > 0 ? release : 1));
|
||||||
remainingSamples -= releaseDelay;
|
remainingSamples -= releaseDelay;
|
||||||
length = min(remainingSamples, release);
|
length = min(remainingSamples, release);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <absl/types/span.h>
|
#include <absl/types/span.h>
|
||||||
|
#include "Helpers.h"
|
||||||
namespace sfz
|
namespace sfz
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -31,6 +32,7 @@ private:
|
||||||
Type sustain { 0 };
|
Type sustain { 0 };
|
||||||
int releaseDelay { 0 };
|
int releaseDelay { 0 };
|
||||||
bool shouldRelease { false };
|
bool shouldRelease { false };
|
||||||
|
LEAK_DETECTOR(ADSREnvelope);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
|
#include "Helpers.h"
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
template <class Type, unsigned int Alignment = SIMDConfig::defaultAlignment>
|
template <class Type, unsigned int Alignment = SIMDConfig::defaultAlignment>
|
||||||
class Buffer
|
class Buffer
|
||||||
{
|
{
|
||||||
|
|
@ -135,4 +135,5 @@ private:
|
||||||
pointer paddedData{nullptr};
|
pointer paddedData{nullptr};
|
||||||
pointer normalEnd{nullptr};
|
pointer normalEnd{nullptr};
|
||||||
pointer _alignedEnd{nullptr};
|
pointer _alignedEnd{nullptr};
|
||||||
|
LEAK_DETECTOR(Buffer);
|
||||||
};
|
};
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include "Helpers.h"
|
||||||
|
|
||||||
namespace sfz
|
namespace sfz
|
||||||
{
|
{
|
||||||
|
|
@ -40,5 +41,6 @@ public:
|
||||||
private:
|
private:
|
||||||
const ValueType defaultValue;
|
const ValueType defaultValue;
|
||||||
std::map<int, ValueType> container;
|
std::map<int, ValueType> container;
|
||||||
|
LEAK_DETECTOR(CCMap);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -67,6 +67,7 @@ struct EGDescription
|
||||||
{
|
{
|
||||||
return ccSwitchedValue(ccValues, ccSustain, sustain) + normalizeCC(velocity)*vel2sustain;
|
return ccSwitchedValue(ccValues, ccSustain, sustain) + normalizeCC(velocity)*vel2sustain;
|
||||||
}
|
}
|
||||||
|
LEAK_DETECTOR(EGDescription);
|
||||||
};
|
};
|
||||||
|
|
||||||
} //namespace sfz
|
} //namespace sfz
|
||||||
|
|
@ -62,6 +62,7 @@ void sfz::FilePool::loadingThread()
|
||||||
auto readBuffer = std::make_unique<Buffer<float>>(fileToLoad.numFrames * 2);
|
auto readBuffer = std::make_unique<Buffer<float>>(fileToLoad.numFrames * 2);
|
||||||
sndFile.readf(readBuffer->data(), fileToLoad.numFrames);
|
sndFile.readf(readBuffer->data(), fileToLoad.numFrames);
|
||||||
fileLoaded->readInterleaved(*readBuffer);
|
fileLoaded->readInterleaved(*readBuffer);
|
||||||
|
ASSERT(fileLoaded != nullptr);
|
||||||
fileToLoad.voice->setFileData(std::move(fileLoaded));
|
fileToLoad.voice->setFileData(std::move(fileLoaded));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,5 +55,6 @@ private:
|
||||||
Buffer<float> tempReadBuffer { config::preloadSize * 2 };
|
Buffer<float> tempReadBuffer { config::preloadSize * 2 };
|
||||||
// std::map<std::string_view, std::shared_ptr<StereoBuffer<float>>> preloadedData;
|
// std::map<std::string_view, std::shared_ptr<StereoBuffer<float>>> preloadedData;
|
||||||
absl::flat_hash_map<std::string_view, std::shared_ptr<StereoBuffer<float>>> preloadedData;
|
absl::flat_hash_map<std::string_view, std::shared_ptr<StereoBuffer<float>>> preloadedData;
|
||||||
|
LEAK_DETECTOR(FilePool);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -1,34 +1,28 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <string_view>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
inline void trimInPlace(std::string_view& s)
|
inline void trimInPlace(std::string_view& s)
|
||||||
{
|
{
|
||||||
const auto leftPosition = s.find_first_not_of(" \r\t\n\f\v");
|
const auto leftPosition = s.find_first_not_of(" \r\t\n\f\v");
|
||||||
if (leftPosition != s.npos)
|
if (leftPosition != s.npos) {
|
||||||
{
|
|
||||||
s.remove_prefix(leftPosition);
|
s.remove_prefix(leftPosition);
|
||||||
const auto rightPosition = s.find_last_not_of(" \r\t\n\f\v");
|
const auto rightPosition = s.find_last_not_of(" \r\t\n\f\v");
|
||||||
s.remove_suffix(s.size() - rightPosition - 1);
|
s.remove_suffix(s.size() - rightPosition - 1);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
s.remove_suffix(s.size());
|
s.remove_suffix(s.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string_view trim(std::string_view s)
|
inline std::string_view trim(std::string_view s)
|
||||||
{
|
{
|
||||||
const auto leftPosition = s.find_first_not_of(" \r\t\n\f\v");
|
const auto leftPosition = s.find_first_not_of(" \r\t\n\f\v");
|
||||||
if (leftPosition != s.npos)
|
if (leftPosition != s.npos) {
|
||||||
{
|
|
||||||
s.remove_prefix(leftPosition);
|
s.remove_prefix(leftPosition);
|
||||||
const auto rightPosition = s.find_last_not_of(" \r\t\n\f\v");
|
const auto rightPosition = s.find_last_not_of(" \r\t\n\f\v");
|
||||||
s.remove_suffix(s.size() - rightPosition - 1);
|
s.remove_suffix(s.size() - rightPosition - 1);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
s.remove_suffix(s.size());
|
s.remove_suffix(s.size());
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
|
|
@ -36,7 +30,7 @@ inline std::string_view trim(std::string_view s)
|
||||||
|
|
||||||
inline constexpr unsigned int Fnv1aBasis = 0x811C9DC5;
|
inline constexpr unsigned int Fnv1aBasis = 0x811C9DC5;
|
||||||
inline constexpr unsigned int Fnv1aPrime = 0x01000193;
|
inline constexpr unsigned int Fnv1aPrime = 0x01000193;
|
||||||
inline constexpr unsigned int hash(const char *s, unsigned int h = Fnv1aBasis)
|
inline constexpr unsigned int hash(const char* s, unsigned int h = Fnv1aBasis)
|
||||||
{
|
{
|
||||||
return !*s ? h : hash(s + 1, static_cast<unsigned int>((h ^ *s) * static_cast<unsigned long long>(Fnv1aPrime)));
|
return !*s ? h : hash(s + 1, static_cast<unsigned int>((h ^ *s) * static_cast<unsigned long long>(Fnv1aPrime)));
|
||||||
}
|
}
|
||||||
|
|
@ -49,72 +43,139 @@ inline unsigned int hash(std::string_view s, unsigned int h = Fnv1aBasis)
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T>
|
||||||
inline constexpr T min(T op1, T op2) { return std::min(op1, op2); }
|
inline constexpr T min(T op1, T op2) { return std::min(op1, op2); }
|
||||||
template<class T>
|
template <class T>
|
||||||
inline constexpr T min(T op1, T op2, T op3) { return std::min(op1, std::min(op2, op3)); }
|
inline constexpr T min(T op1, T op2, T op3) { return std::min(op1, std::min(op2, op3)); }
|
||||||
template<class T>
|
template <class T>
|
||||||
inline constexpr T min(T op1, T op2, T op3, T op4) { return std::min(op1, std::min(op2, std::min(op3, op4))); }
|
inline constexpr T min(T op1, T op2, T op3, T op4) { return std::min(op1, std::min(op2, std::min(op3, op4))); }
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#if __linux__ || __unix__
|
#if __linux__ || __unix__
|
||||||
// These trap into the signal library rather than your own sourcecode
|
// These trap into the signal library rather than your own sourcecode
|
||||||
// #define ASSERTFALSE { ::kill(0, SIGTRAP); }
|
// #define ASSERTFALSE { ::kill(0, SIGTRAP); }
|
||||||
// #define ASSERTFALSE { raise(SIGTRAP); }
|
// #define ASSERTFALSE { raise(SIGTRAP); }
|
||||||
#define ASSERTFALSE { __asm__("int3"); }
|
#define ASSERTFALSE \
|
||||||
|
{ \
|
||||||
|
__asm__("int3"); \
|
||||||
|
}
|
||||||
#elif _WIN32 || _WIN64
|
#elif _WIN32 || _WIN64
|
||||||
#pragma intrinsic (__debugbreak)
|
#pragma intrinsic(__debugbreak)
|
||||||
#define ASSERTFALSE { __debugbreak(); }
|
#define ASSERTFALSE \
|
||||||
|
{ \
|
||||||
|
__debugbreak(); \
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#define ASSERTFALSE { __asm int 3; }
|
#define ASSERTFALSE \
|
||||||
|
{ \
|
||||||
|
__asm int 3; \
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#define ASSERT(expression) if (!(expression)) ASSERTFALSE
|
#define ASSERT(expression) \
|
||||||
|
if (!(expression)) \
|
||||||
|
ASSERTFALSE
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#define DBG(ostream) std::cerr << ostream << '\n'
|
#define DBG(ostream) std::cerr << ostream << '\n'
|
||||||
#else
|
#else
|
||||||
#define ASSERTFALSE
|
#define ASSERTFALSE
|
||||||
#define ASSERT(expression)
|
#define ASSERT(expression)
|
||||||
#define DBG(ostream)
|
#define DBG(ostream)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<class Type>
|
template <class Type>
|
||||||
inline constexpr Type db2pow(Type in)
|
inline constexpr Type db2pow(Type in)
|
||||||
{
|
{
|
||||||
return std::pow(static_cast<Type>(10.0), in * static_cast<Type>(0.1));
|
return std::pow(static_cast<Type>(10.0), in * static_cast<Type>(0.1));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template <class Type>
|
||||||
inline constexpr Type pow2db(Type in)
|
inline constexpr Type pow2db(Type in)
|
||||||
{
|
{
|
||||||
return static_cast<Type>(10.0) * std::log10(in);
|
return static_cast<Type>(10.0) * std::log10(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template <class Type>
|
||||||
inline constexpr Type db2mag(Type in)
|
inline constexpr Type db2mag(Type in)
|
||||||
{
|
{
|
||||||
return std::pow(static_cast<Type>(10.0), in * static_cast<Type>(0.05));
|
return std::pow(static_cast<Type>(10.0), in * static_cast<Type>(0.05));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template <class Type>
|
||||||
inline constexpr Type mag2db(Type in)
|
inline constexpr Type mag2db(Type in)
|
||||||
{
|
{
|
||||||
return static_cast<Type>(20.0) * std::log10(in);
|
return static_cast<Type>(20.0) * std::log10(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Random
|
namespace Random {
|
||||||
{
|
static inline std::random_device randomDevice;
|
||||||
static inline std::random_device randomDevice;
|
static inline std::mt19937 randomGenerator { randomDevice() };
|
||||||
static inline std::mt19937 randomGenerator { randomDevice() };
|
}
|
||||||
}
|
|
||||||
|
|
||||||
inline float midiNoteFrequency(const int noteNumber)
|
inline float midiNoteFrequency(const int noteNumber)
|
||||||
{
|
{
|
||||||
return 440.0f * std::pow(2.0f, (noteNumber - 69) / 12.0f);
|
return 440.0f * std::pow(2.0f, (noteNumber - 69) / 12.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template <class Type>
|
||||||
constexpr Type pi { 3.141592653589793238462643383279502884 };
|
constexpr Type pi { 3.141592653589793238462643383279502884 };
|
||||||
template<class Type>
|
template <class Type>
|
||||||
constexpr Type twoPi { 2*pi<Type> };
|
constexpr Type twoPi { 2 * pi<Type> };
|
||||||
template<class Type>
|
template <class Type>
|
||||||
constexpr Type piTwo { pi<Type>/2 };
|
constexpr Type piTwo { pi<Type> / 2 };
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
template <class Owner>
|
||||||
|
class LeakDetector {
|
||||||
|
public:
|
||||||
|
LeakDetector()
|
||||||
|
{
|
||||||
|
// auto currentCounter = objectCounter.count.load();
|
||||||
|
// auto desiredCounter = currentCounter + 1;
|
||||||
|
// while(!objectCounter.count.compare_exchange_weak(currentCounter, desiredCounter))
|
||||||
|
// desiredCounter = currentCounter + 1;
|
||||||
|
objectCounter.count++;
|
||||||
|
// DBG("Counted " << desiredCounter << " " << Owner::getClassName());
|
||||||
|
}
|
||||||
|
LeakDetector(const LeakDetector&)
|
||||||
|
{
|
||||||
|
objectCounter.count++;
|
||||||
|
}
|
||||||
|
~LeakDetector()
|
||||||
|
{
|
||||||
|
objectCounter.count--;
|
||||||
|
// auto currentCounter = objectCounter.count.load();
|
||||||
|
// auto desiredCounter = currentCounter - 1;
|
||||||
|
// while(!objectCounter.count.compare_exchange_weak(currentCounter, desiredCounter))
|
||||||
|
// desiredCounter = currentCounter - 1;
|
||||||
|
// DBG("Counted " << desiredCounter << " " << Owner::getClassName() << " left after deletion");
|
||||||
|
if (objectCounter.count.load() < 0) {
|
||||||
|
DBG("Deleted a dangling pointer for class " << Owner::getClassName());
|
||||||
|
// Deleted a dangling pointer!
|
||||||
|
// ASSERTFALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ObjectCounter {
|
||||||
|
ObjectCounter() = default;
|
||||||
|
~ObjectCounter()
|
||||||
|
{
|
||||||
|
if (auto residualCount = count.load() > 0) {
|
||||||
|
DBG("Leaked " << residualCount << " instance(s) of class " << Owner::getClassName());
|
||||||
|
// Leaked ojects
|
||||||
|
// ASSERTFALSE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std::atomic<int> count { 0 };
|
||||||
|
};
|
||||||
|
static inline ObjectCounter objectCounter;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define LEAK_DETECTOR(Class) \
|
||||||
|
friend class LeakDetector<Class>; \
|
||||||
|
static const char* getClassName() noexcept { return #Class; } \
|
||||||
|
LeakDetector<Class> leakDetector;
|
||||||
|
#else
|
||||||
|
#define LEAK_DETECTOR(Class)
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
|
#include "Helpers.h"
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <absl/types/span.h>
|
#include <absl/types/span.h>
|
||||||
|
|
@ -25,6 +26,7 @@ private:
|
||||||
std::vector<std::pair<int, Type>> events;
|
std::vector<std::pair<int, Type>> events;
|
||||||
int maxCapacity { config::defaultSamplesPerBlock };
|
int maxCapacity { config::defaultSamplesPerBlock };
|
||||||
Type currentValue { 0.0 };
|
Type currentValue { 0.0 };
|
||||||
|
LEAK_DETECTOR(LinearEnvelope);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -125,12 +125,15 @@ int sampleRateChanged(jack_nframes_t nframes, void* arg [[maybe_unused]])
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool shouldClose { false };
|
||||||
|
|
||||||
static void done(int sig [[maybe_unused]])
|
static void done(int sig [[maybe_unused]])
|
||||||
{
|
{
|
||||||
std::cout << "Closing..." << '\n';
|
std::cout << "Signal received" << '\n';
|
||||||
if (client != nullptr)
|
shouldClose = true;
|
||||||
jack_client_close(client);
|
// if (client != nullptr)
|
||||||
exit(0);
|
|
||||||
|
// exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
|
|
@ -229,6 +232,11 @@ int main(int argc, char** argv)
|
||||||
signal(SIGINT, done);
|
signal(SIGINT, done);
|
||||||
signal(SIGTERM, done);
|
signal(SIGTERM, done);
|
||||||
signal(SIGQUIT, done);
|
signal(SIGQUIT, done);
|
||||||
sleep(-1);
|
|
||||||
|
while (!shouldClose)
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
std::cout << "Closing..." << '\n';
|
||||||
|
jack_client_close(client);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ struct Opcode
|
||||||
std::string_view value{};
|
std::string_view value{};
|
||||||
// This is to handle the integer parameter of some opcodes
|
// This is to handle the integer parameter of some opcodes
|
||||||
std::optional<uint8_t> parameter;
|
std::optional<uint8_t> parameter;
|
||||||
|
LEAK_DETECTOR(Opcode);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class ValueType>
|
template<class ValueType>
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,7 @@ private:
|
||||||
std::uniform_real_distribution<float> gainDistribution { -sfz::Default::ampRandom, sfz::Default::ampRandom };
|
std::uniform_real_distribution<float> gainDistribution { -sfz::Default::ampRandom, sfz::Default::ampRandom };
|
||||||
std::uniform_real_distribution<float> delayDistribution { 0, sfz::Default::delayRandom };
|
std::uniform_real_distribution<float> delayDistribution { 0, sfz::Default::delayRandom };
|
||||||
std::uniform_int_distribution<uint32_t> offsetDistribution { 0, sfz::Default::offsetRandom };
|
std::uniform_int_distribution<uint32_t> offsetDistribution { 0, sfz::Default::offsetRandom };
|
||||||
|
LEAK_DETECTOR(Region);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sfz
|
} // namespace sfz
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "Helpers.h"
|
#include "Helpers.h"
|
||||||
|
|
||||||
#include "SIMDHelpers.h"
|
#include "SIMDHelpers.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
@ -160,4 +159,5 @@ private:
|
||||||
int numFrames { 0 };
|
int numFrames { 0 };
|
||||||
Buffer<Type, Alignment> leftBuffer {};
|
Buffer<Type, Alignment> leftBuffer {};
|
||||||
Buffer<Type, Alignment> rightBuffer {};
|
Buffer<Type, Alignment> rightBuffer {};
|
||||||
|
LEAK_DETECTOR(StereoBuffer);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -141,4 +141,5 @@ private:
|
||||||
size_t numFrames { 0 };
|
size_t numFrames { 0 };
|
||||||
absl::Span<Type> leftBuffer;
|
absl::Span<Type> leftBuffer;
|
||||||
absl::Span<Type> rightBuffer;
|
absl::Span<Type> rightBuffer;
|
||||||
|
LEAK_DETECTOR(StereoSpan);
|
||||||
};
|
};
|
||||||
|
|
@ -2,14 +2,17 @@
|
||||||
#include "FilePool.h"
|
#include "FilePool.h"
|
||||||
#include "Parser.h"
|
#include "Parser.h"
|
||||||
#include "Region.h"
|
#include "Region.h"
|
||||||
#include "StereoSpan.h"
|
|
||||||
#include "SfzHelpers.h"
|
#include "SfzHelpers.h"
|
||||||
|
#include "StereoSpan.h"
|
||||||
#include "absl/types/span.h"
|
#include "absl/types/span.h"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <chrono>
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
|
|
||||||
|
|
@ -20,6 +23,13 @@ public:
|
||||||
for (int i = 0; i < config::numVoices; ++i)
|
for (int i = 0; i < config::numVoices; ++i)
|
||||||
voices.push_back(std::make_unique<Voice>(ccState));
|
voices.push_back(std::make_unique<Voice>(ccState));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~Synth()
|
||||||
|
{
|
||||||
|
threadsShouldQuit = true;
|
||||||
|
garbageCollectionThread.join();
|
||||||
|
}
|
||||||
|
|
||||||
bool loadSfzFile(const std::filesystem::path& file) final;
|
bool loadSfzFile(const std::filesystem::path& file) final;
|
||||||
int getNumRegions() const noexcept { return static_cast<int>(regions.size()); }
|
int getNumRegions() const noexcept { return static_cast<int>(regions.size()); }
|
||||||
int getNumGroups() const noexcept { return numGroups; }
|
int getNumGroups() const noexcept { return numGroups; }
|
||||||
|
|
@ -34,7 +44,7 @@ public:
|
||||||
DBG("[Synth] Samples per block set to " << samplesPerBlock);
|
DBG("[Synth] Samples per block set to " << samplesPerBlock);
|
||||||
this->samplesPerBlock = samplesPerBlock;
|
this->samplesPerBlock = samplesPerBlock;
|
||||||
this->tempBuffer.resize(samplesPerBlock);
|
this->tempBuffer.resize(samplesPerBlock);
|
||||||
for (auto & voice: voices)
|
for (auto& voice : voices)
|
||||||
voice->setSamplesPerBlock(samplesPerBlock);
|
voice->setSamplesPerBlock(samplesPerBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,14 +52,14 @@ public:
|
||||||
{
|
{
|
||||||
DBG("[Synth] Sample rate set to " << sampleRate);
|
DBG("[Synth] Sample rate set to " << sampleRate);
|
||||||
this->sampleRate = sampleRate;
|
this->sampleRate = sampleRate;
|
||||||
for (auto & voice: voices)
|
for (auto& voice : voices)
|
||||||
voice->setSampleRate(sampleRate);
|
voice->setSampleRate(sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderBlock(StereoSpan<float> buffer)
|
void renderBlock(StereoSpan<float> buffer)
|
||||||
{
|
{
|
||||||
buffer.fill(0.0f);
|
buffer.fill(0.0f);
|
||||||
|
|
||||||
StereoSpan<float> tempSpan { tempBuffer, buffer.size() };
|
StereoSpan<float> tempSpan { tempBuffer, buffer.size() };
|
||||||
for (auto& voice : voices) {
|
for (auto& voice : voices) {
|
||||||
voice->renderBlock(tempSpan);
|
voice->renderBlock(tempSpan);
|
||||||
|
|
@ -63,8 +73,7 @@ public:
|
||||||
|
|
||||||
for (auto& region : regions) {
|
for (auto& region : regions) {
|
||||||
if (region->registerNoteOn(channel, noteNumber, velocity, randValue)) {
|
if (region->registerNoteOn(channel, noteNumber, velocity, randValue)) {
|
||||||
for (auto& voice: voices)
|
for (auto& voice : voices) {
|
||||||
{
|
|
||||||
if (voice->checkOffGroup(delay, region->group))
|
if (voice->checkOffGroup(delay, region->group))
|
||||||
noteOff(delay, voice->getTriggerChannel(), voice->getTriggerNumber(), 0);
|
noteOff(delay, voice->getTriggerChannel(), voice->getTriggerNumber(), 0);
|
||||||
}
|
}
|
||||||
|
|
@ -164,10 +173,21 @@ private:
|
||||||
std::mt19937 randomGenerator { rd() };
|
std::mt19937 randomGenerator { rd() };
|
||||||
std::uniform_real_distribution<float> randomDistribution { 0, 1 };
|
std::uniform_real_distribution<float> randomDistribution { 0, 1 };
|
||||||
|
|
||||||
|
bool threadsShouldQuit { false };
|
||||||
|
std::thread garbageCollectionThread { [&]() {
|
||||||
|
while (!threadsShouldQuit) {
|
||||||
|
for (auto& voice : voices)
|
||||||
|
voice->garbageCollect();
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
|
}
|
||||||
|
} };
|
||||||
|
|
||||||
float getUniform()
|
float getUniform()
|
||||||
{
|
{
|
||||||
return randomDistribution(randomGenerator);
|
return randomDistribution(randomGenerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LEAK_DETECTOR(Synth);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -65,8 +65,8 @@ public:
|
||||||
|
|
||||||
void setFileData(std::unique_ptr<StereoBuffer<float>> file)
|
void setFileData(std::unique_ptr<StereoBuffer<float>> file)
|
||||||
{
|
{
|
||||||
fileData.reset(file.release());
|
fileData = std::move(file);
|
||||||
dataReady.store(true);
|
dataReady.store(true, std::memory_order_seq_cst);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFree()
|
bool isFree()
|
||||||
|
|
@ -76,12 +76,19 @@ public:
|
||||||
|
|
||||||
void registerNoteOff(int delay, int channel, int noteNumber, uint8_t velocity [[maybe_unused]])
|
void registerNoteOff(int delay, int channel, int noteNumber, uint8_t velocity [[maybe_unused]])
|
||||||
{
|
{
|
||||||
if (state == State::playing && triggerChannel == channel && triggerNumber == noteNumber)
|
if (state == State::playing && triggerChannel == channel && triggerNumber == noteNumber) {
|
||||||
egEnvelope.startRelease(delay);
|
noteIsOff = true;
|
||||||
|
|
||||||
|
if (ccState[64] < 63)
|
||||||
|
egEnvelope.startRelease(delay);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerCC(int delay [[maybe_unused]], int channel [[maybe_unused]], int ccNumber [[maybe_unused]], uint8_t ccValue [[maybe_unused]])
|
void registerCC(int delay [[maybe_unused]], int channel [[maybe_unused]], int ccNumber [[maybe_unused]], uint8_t ccValue [[maybe_unused]])
|
||||||
{
|
{
|
||||||
|
if (ccNumber == 64 && noteIsOff && ccValue < 63)
|
||||||
|
egEnvelope.startRelease(delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerPitchWheel(int delay, int channel, int pitch);
|
void registerPitchWheel(int delay, int channel, int pitch);
|
||||||
|
|
@ -126,7 +133,7 @@ public:
|
||||||
void fillWithData(StereoSpan<float> buffer)
|
void fillWithData(StereoSpan<float> buffer)
|
||||||
{
|
{
|
||||||
const StereoSpan<const float> source([&]() -> StereoBuffer<float>& {
|
const StereoSpan<const float> source([&]() -> StereoBuffer<float>& {
|
||||||
if (dataReady)
|
if (dataReady.load(std::memory_order_seq_cst) && fileData != nullptr)
|
||||||
return *fileData;
|
return *fileData;
|
||||||
else
|
else
|
||||||
return *region->preloadedData;
|
return *region->preloadedData;
|
||||||
|
|
@ -135,6 +142,7 @@ public:
|
||||||
const auto actualBlockSize = min(buffer.size(), source.size() - sourcePosition);
|
const auto actualBlockSize = min(buffer.size(), source.size() - sourcePosition);
|
||||||
buffer.add(source.subspan(sourcePosition, actualBlockSize));
|
buffer.add(source.subspan(sourcePosition, actualBlockSize));
|
||||||
sourcePosition += actualBlockSize;
|
sourcePosition += actualBlockSize;
|
||||||
|
|
||||||
if (sourcePosition == source.size())
|
if (sourcePosition == source.size())
|
||||||
egEnvelope.startRelease(buffer.size());
|
egEnvelope.startRelease(buffer.size());
|
||||||
}
|
}
|
||||||
|
|
@ -189,6 +197,13 @@ public:
|
||||||
region = nullptr;
|
region = nullptr;
|
||||||
state = State::idle;
|
state = State::idle;
|
||||||
dataReady.store(false);
|
dataReady.store(false);
|
||||||
|
noteIsOff = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void garbageCollect()
|
||||||
|
{
|
||||||
|
if (state == State::idle && region == nullptr)
|
||||||
|
fileData.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -200,6 +215,7 @@ private:
|
||||||
release
|
release
|
||||||
};
|
};
|
||||||
State state;
|
State state;
|
||||||
|
bool noteIsOff { false };
|
||||||
|
|
||||||
TriggerType triggerType;
|
TriggerType triggerType;
|
||||||
int triggerNumber;
|
int triggerNumber;
|
||||||
|
|
@ -214,7 +230,7 @@ private:
|
||||||
uint32_t sourcePosition;
|
uint32_t sourcePosition;
|
||||||
uint32_t initialDelay;
|
uint32_t initialDelay;
|
||||||
|
|
||||||
std::atomic<bool> dataReady;
|
std::atomic<bool> dataReady { false };
|
||||||
std::unique_ptr<StereoBuffer<float>> fileData;
|
std::unique_ptr<StereoBuffer<float>> fileData;
|
||||||
|
|
||||||
Buffer<float> tempBuffer1;
|
Buffer<float> tempBuffer1;
|
||||||
|
|
@ -227,6 +243,7 @@ private:
|
||||||
|
|
||||||
const CCValueArray& ccState;
|
const CCValueArray& ccState;
|
||||||
ADSREnvelope<float> egEnvelope;
|
ADSREnvelope<float> egEnvelope;
|
||||||
|
LEAK_DETECTOR(Voice);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sfz
|
} // namespace sfz
|
||||||
Loading…
Add table
Reference in a new issue