Added a leak detector and corrected some bugs

This commit is contained in:
paulfd 2019-08-25 00:00:50 +02:00
parent 99c3e8878f
commit dba851385d
16 changed files with 181 additions and 61 deletions

View file

@ -181,7 +181,8 @@ void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
}
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));
remainingSamples -= releaseDelay;
length = min(remainingSamples, release);

View file

@ -1,5 +1,6 @@
#pragma once
#include <absl/types/span.h>
#include "Helpers.h"
namespace sfz
{
@ -31,6 +32,7 @@ private:
Type sustain { 0 };
int releaseDelay { 0 };
bool shouldRelease { false };
LEAK_DETECTOR(ADSREnvelope);
};
}

View file

@ -1,11 +1,11 @@
#pragma once
#include "Globals.h"
#include "Helpers.h"
#include <cstdlib>
#include <cstring>
#include <memory>
#include <type_traits>
#include <utility>
template <class Type, unsigned int Alignment = SIMDConfig::defaultAlignment>
class Buffer
{
@ -135,4 +135,5 @@ private:
pointer paddedData{nullptr};
pointer normalEnd{nullptr};
pointer _alignedEnd{nullptr};
LEAK_DETECTOR(Buffer);
};

View file

@ -1,5 +1,6 @@
#pragma once
#include <map>
#include "Helpers.h"
namespace sfz
{
@ -40,5 +41,6 @@ public:
private:
const ValueType defaultValue;
std::map<int, ValueType> container;
LEAK_DETECTOR(CCMap);
};
}

View file

@ -67,6 +67,7 @@ struct EGDescription
{
return ccSwitchedValue(ccValues, ccSustain, sustain) + normalizeCC(velocity)*vel2sustain;
}
LEAK_DETECTOR(EGDescription);
};
} //namespace sfz

View file

@ -62,6 +62,7 @@ void sfz::FilePool::loadingThread()
auto readBuffer = std::make_unique<Buffer<float>>(fileToLoad.numFrames * 2);
sndFile.readf(readBuffer->data(), fileToLoad.numFrames);
fileLoaded->readInterleaved(*readBuffer);
ASSERT(fileLoaded != nullptr);
fileToLoad.voice->setFileData(std::move(fileLoaded));
}
}

View file

@ -55,5 +55,6 @@ private:
Buffer<float> tempReadBuffer { config::preloadSize * 2 };
// std::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);
};
}

View file

@ -1,34 +1,28 @@
#pragma once
#include <string_view>
#include <signal.h>
#include <random>
#include <signal.h>
#include <string_view>
inline void trimInPlace(std::string_view& s)
{
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);
const auto rightPosition = s.find_last_not_of(" \r\t\n\f\v");
s.remove_suffix(s.size() - rightPosition - 1);
}
else
{
} else {
s.remove_suffix(s.size());
}
}
}
inline std::string_view trim(std::string_view s)
{
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);
const auto rightPosition = s.find_last_not_of(" \r\t\n\f\v");
s.remove_suffix(s.size() - rightPosition - 1);
}
else
{
} else {
s.remove_suffix(s.size());
}
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 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)));
}
@ -49,72 +43,139 @@ inline unsigned int hash(std::string_view s, unsigned int h = Fnv1aBasis)
return h;
}
template<class T>
template <class T>
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)); }
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))); }
#ifndef NDEBUG
#if __linux__ || __unix__
// These trap into the signal library rather than your own sourcecode
// #define ASSERTFALSE { ::kill(0, SIGTRAP); }
// #define ASSERTFALSE { raise(SIGTRAP); }
#define ASSERTFALSE { __asm__("int3"); }
// These trap into the signal library rather than your own sourcecode
// #define ASSERTFALSE { ::kill(0, SIGTRAP); }
// #define ASSERTFALSE { raise(SIGTRAP); }
#define ASSERTFALSE \
{ \
__asm__("int3"); \
}
#elif _WIN32 || _WIN64
#pragma intrinsic (__debugbreak)
#define ASSERTFALSE { __debugbreak(); }
#pragma intrinsic(__debugbreak)
#define ASSERTFALSE \
{ \
__debugbreak(); \
}
#else
#define ASSERTFALSE { __asm int 3; }
#define ASSERTFALSE \
{ \
__asm int 3; \
}
#endif
#define ASSERT(expression) if (!(expression)) ASSERTFALSE
#define ASSERT(expression) \
if (!(expression)) \
ASSERTFALSE
#include <iostream>
#define DBG(ostream) std::cerr << ostream << '\n'
#else
#define ASSERTFALSE
#define ASSERTFALSE
#define ASSERT(expression)
#define DBG(ostream)
#endif
template<class Type>
template <class Type>
inline constexpr Type db2pow(Type in)
{
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)
{
return static_cast<Type>(10.0) * std::log10(in);
}
template<class Type>
template <class Type>
inline constexpr Type db2mag(Type in)
{
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)
{
return static_cast<Type>(20.0) * std::log10(in);
}
namespace Random
{
static inline std::random_device randomDevice;
static inline std::mt19937 randomGenerator { randomDevice() };
}
namespace Random {
static inline std::random_device 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);
}
template<class Type>
template <class Type>
constexpr Type pi { 3.141592653589793238462643383279502884 };
template<class Type>
constexpr Type twoPi { 2*pi<Type> };
template<class Type>
constexpr Type piTwo { pi<Type>/2 };
template <class Type>
constexpr Type twoPi { 2 * pi<Type> };
template <class Type>
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

View file

@ -1,5 +1,6 @@
#pragma once
#include "Globals.h"
#include "Helpers.h"
#include <type_traits>
#include <functional>
#include <absl/types/span.h>
@ -25,6 +26,7 @@ private:
std::vector<std::pair<int, Type>> events;
int maxCapacity { config::defaultSamplesPerBlock };
Type currentValue { 0.0 };
LEAK_DETECTOR(LinearEnvelope);
};
}

View file

@ -125,12 +125,15 @@ int sampleRateChanged(jack_nframes_t nframes, void* arg [[maybe_unused]])
return 0;
}
static bool shouldClose { false };
static void done(int sig [[maybe_unused]])
{
std::cout << "Closing..." << '\n';
if (client != nullptr)
jack_client_close(client);
exit(0);
std::cout << "Signal received" << '\n';
shouldClose = true;
// if (client != nullptr)
// exit(0);
}
int main(int argc, char** argv)
@ -229,6 +232,11 @@ int main(int argc, char** argv)
signal(SIGINT, done);
signal(SIGTERM, done);
signal(SIGQUIT, done);
sleep(-1);
while (!shouldClose)
sleep(1);
std::cout << "Closing..." << '\n';
jack_client_close(client);
return 0;
}

View file

@ -19,6 +19,7 @@ struct Opcode
std::string_view value{};
// This is to handle the integer parameter of some opcodes
std::optional<uint8_t> parameter;
LEAK_DETECTOR(Opcode);
};
template<class ValueType>

View file

@ -153,6 +153,7 @@ private:
std::uniform_real_distribution<float> gainDistribution { -sfz::Default::ampRandom, sfz::Default::ampRandom };
std::uniform_real_distribution<float> delayDistribution { 0, sfz::Default::delayRandom };
std::uniform_int_distribution<uint32_t> offsetDistribution { 0, sfz::Default::offsetRandom };
LEAK_DETECTOR(Region);
};
} // namespace sfz

View file

@ -2,7 +2,6 @@
#include "Buffer.h"
#include "Globals.h"
#include "Helpers.h"
#include "SIMDHelpers.h"
#include <array>
#include <iostream>
@ -160,4 +159,5 @@ private:
int numFrames { 0 };
Buffer<Type, Alignment> leftBuffer {};
Buffer<Type, Alignment> rightBuffer {};
LEAK_DETECTOR(StereoBuffer);
};

View file

@ -141,4 +141,5 @@ private:
size_t numFrames { 0 };
absl::Span<Type> leftBuffer;
absl::Span<Type> rightBuffer;
LEAK_DETECTOR(StereoSpan);
};

View file

@ -2,14 +2,17 @@
#include "FilePool.h"
#include "Parser.h"
#include "Region.h"
#include "StereoSpan.h"
#include "SfzHelpers.h"
#include "StereoSpan.h"
#include "absl/types/span.h"
#include <optional>
#include <random>
#include <set>
#include <string_view>
#include <thread>
#include <vector>
#include <chrono>
using namespace std::literals;
namespace sfz {
@ -20,6 +23,13 @@ public:
for (int i = 0; i < config::numVoices; ++i)
voices.push_back(std::make_unique<Voice>(ccState));
}
~Synth()
{
threadsShouldQuit = true;
garbageCollectionThread.join();
}
bool loadSfzFile(const std::filesystem::path& file) final;
int getNumRegions() const noexcept { return static_cast<int>(regions.size()); }
int getNumGroups() const noexcept { return numGroups; }
@ -34,7 +44,7 @@ public:
DBG("[Synth] Samples per block set to " << samplesPerBlock);
this->samplesPerBlock = samplesPerBlock;
this->tempBuffer.resize(samplesPerBlock);
for (auto & voice: voices)
for (auto& voice : voices)
voice->setSamplesPerBlock(samplesPerBlock);
}
@ -42,14 +52,14 @@ public:
{
DBG("[Synth] Sample rate set to " << sampleRate);
this->sampleRate = sampleRate;
for (auto & voice: voices)
for (auto& voice : voices)
voice->setSampleRate(sampleRate);
}
void renderBlock(StereoSpan<float> buffer)
{
buffer.fill(0.0f);
StereoSpan<float> tempSpan { tempBuffer, buffer.size() };
for (auto& voice : voices) {
voice->renderBlock(tempSpan);
@ -63,8 +73,7 @@ public:
for (auto& region : regions) {
if (region->registerNoteOn(channel, noteNumber, velocity, randValue)) {
for (auto& voice: voices)
{
for (auto& voice : voices) {
if (voice->checkOffGroup(delay, region->group))
noteOff(delay, voice->getTriggerChannel(), voice->getTriggerNumber(), 0);
}
@ -164,10 +173,21 @@ private:
std::mt19937 randomGenerator { rd() };
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()
{
return randomDistribution(randomGenerator);
}
LEAK_DETECTOR(Synth);
};
}

View file

@ -65,8 +65,8 @@ public:
void setFileData(std::unique_ptr<StereoBuffer<float>> file)
{
fileData.reset(file.release());
dataReady.store(true);
fileData = std::move(file);
dataReady.store(true, std::memory_order_seq_cst);
}
bool isFree()
@ -76,12 +76,19 @@ public:
void registerNoteOff(int delay, int channel, int noteNumber, uint8_t velocity [[maybe_unused]])
{
if (state == State::playing && triggerChannel == channel && triggerNumber == noteNumber)
egEnvelope.startRelease(delay);
if (state == State::playing && triggerChannel == channel && triggerNumber == noteNumber) {
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]])
{
if (ccNumber == 64 && noteIsOff && ccValue < 63)
egEnvelope.startRelease(delay);
}
void registerPitchWheel(int delay, int channel, int pitch);
@ -126,7 +133,7 @@ public:
void fillWithData(StereoSpan<float> buffer)
{
const StereoSpan<const float> source([&]() -> StereoBuffer<float>& {
if (dataReady)
if (dataReady.load(std::memory_order_seq_cst) && fileData != nullptr)
return *fileData;
else
return *region->preloadedData;
@ -135,6 +142,7 @@ public:
const auto actualBlockSize = min(buffer.size(), source.size() - sourcePosition);
buffer.add(source.subspan(sourcePosition, actualBlockSize));
sourcePosition += actualBlockSize;
if (sourcePosition == source.size())
egEnvelope.startRelease(buffer.size());
}
@ -189,6 +197,13 @@ public:
region = nullptr;
state = State::idle;
dataReady.store(false);
noteIsOff = false;
}
void garbageCollect()
{
if (state == State::idle && region == nullptr)
fileData.reset();
}
private:
@ -200,6 +215,7 @@ private:
release
};
State state;
bool noteIsOff { false };
TriggerType triggerType;
int triggerNumber;
@ -214,7 +230,7 @@ private:
uint32_t sourcePosition;
uint32_t initialDelay;
std::atomic<bool> dataReady;
std::atomic<bool> dataReady { false };
std::unique_ptr<StereoBuffer<float>> fileData;
Buffer<float> tempBuffer1;
@ -227,6 +243,7 @@ private:
const CCValueArray& ccState;
ADSREnvelope<float> egEnvelope;
LEAK_DETECTOR(Voice);
};
} // namespace sfz