Merge branch 'bugfix/counting-buffers' into develop
This commit is contained in:
commit
3056f20a35
8 changed files with 170 additions and 44 deletions
|
|
@ -273,8 +273,11 @@ int main(int argc, char** argv)
|
||||||
signal(SIGQUIT, done);
|
signal(SIGQUIT, done);
|
||||||
|
|
||||||
while (!shouldClose){
|
while (!shouldClose){
|
||||||
// synth.garbageCollect();
|
#ifndef NDEBUG
|
||||||
std::this_thread::sleep_for(1s);
|
std::cout << "Allocated buffers: " << synth.getAllocatedBuffers() << '\n';
|
||||||
|
std::cout << "Total size: " << synth.getAllocatedBytes() << '\n';
|
||||||
|
#endif
|
||||||
|
std::this_thread::sleep_for(2s);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Closing..." << '\n';
|
std::cout << "Closing..." << '\n';
|
||||||
|
|
|
||||||
14
lv2/sfizz.c
14
lv2/sfizz.c
|
|
@ -63,6 +63,7 @@
|
||||||
#define DEFAULT_VOICES 64
|
#define DEFAULT_VOICES 64
|
||||||
#define DEFAULT_OVERSAMPLING SFIZZ_OVERSAMPLING_X1
|
#define DEFAULT_OVERSAMPLING SFIZZ_OVERSAMPLING_X1
|
||||||
#define DEFAULT_PRELOAD 8192
|
#define DEFAULT_PRELOAD 8192
|
||||||
|
#define LOG_SAMPLE_COUNT 96000
|
||||||
#define UNUSED(x) (void)(x)
|
#define UNUSED(x) (void)(x)
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
|
@ -121,6 +122,7 @@ typedef struct
|
||||||
sfizz_oversampling_factor_t oversampling;
|
sfizz_oversampling_factor_t oversampling;
|
||||||
bool changing_state;
|
bool changing_state;
|
||||||
int max_block_size;
|
int max_block_size;
|
||||||
|
int sample_counter;
|
||||||
float sample_rate;
|
float sample_rate;
|
||||||
} sfizz_plugin_t;
|
} sfizz_plugin_t;
|
||||||
|
|
||||||
|
|
@ -226,6 +228,7 @@ instantiate(const LV2_Descriptor *descriptor,
|
||||||
self->oversampling = DEFAULT_OVERSAMPLING;
|
self->oversampling = DEFAULT_OVERSAMPLING;
|
||||||
self->preload_size = DEFAULT_PRELOAD;
|
self->preload_size = DEFAULT_PRELOAD;
|
||||||
self->changing_state = false;
|
self->changing_state = false;
|
||||||
|
self->sample_counter = 0;
|
||||||
|
|
||||||
// Get the features from the host and populate the structure
|
// Get the features from the host and populate the structure
|
||||||
for (const LV2_Feature *const *f = features; *f; f++)
|
for (const LV2_Feature *const *f = features; *f; f++)
|
||||||
|
|
@ -575,6 +578,17 @@ run(LV2_Handle instance, uint32_t sample_count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// Log the buffer usage
|
||||||
|
self->sample_counter += (int)sample_count;
|
||||||
|
if (self->sample_counter > LOG_SAMPLE_COUNT)
|
||||||
|
{
|
||||||
|
lv2_log_note(&self->logger, "[run] Allocated buffers: %d\n", sfizz_get_num_buffers(self->synth));
|
||||||
|
lv2_log_note(&self->logger, "[run] Allocated bytes: %d\n", sfizz_get_num_bytes(self->synth));
|
||||||
|
self->sample_counter -= LOG_SAMPLE_COUNT;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Render the block
|
// Render the block
|
||||||
sfizz_render_block(self->synth, self->output_buffers, 2, (int)sample_count);
|
sfizz_render_block(self->synth, self->output_buffers, 2, (int)sample_count);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
src/sfizz.h
20
src/sfizz.h
|
|
@ -305,6 +305,26 @@ void sfizz_set_num_voices(sfizz_synth_t* synth, int num_voices);
|
||||||
*/
|
*/
|
||||||
int sfizz_get_num_voices(sfizz_synth_t* synth);
|
int sfizz_get_num_voices(sfizz_synth_t* synth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of allocated buffers from the synth.
|
||||||
|
*
|
||||||
|
* @param synth The synth
|
||||||
|
*
|
||||||
|
* @return The number of buffers held by the synth
|
||||||
|
*/
|
||||||
|
int sfizz_get_num_buffers(sfizz_synth_t* synth);
|
||||||
|
/**
|
||||||
|
* @brief Get the number of bytes allocated from the synth. Note that this
|
||||||
|
* value can be less than the actual memory usage since it only
|
||||||
|
* counts the buffer objects managed by sfizz.
|
||||||
|
*
|
||||||
|
* @param synth The synth
|
||||||
|
*
|
||||||
|
* @return The number of bytes held by the synth in buffers;
|
||||||
|
*/
|
||||||
|
int sfizz_get_num_bytes(sfizz_synth_t* synth);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -29,20 +29,65 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#ifdef DEBUG
|
||||||
|
#include <iostream>
|
||||||
|
#endif
|
||||||
namespace sfz
|
namespace sfz
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A heap buffer structure that tries to align its beginning and adds a small offset
|
* @brief A buffer counting class that tries to track the memory usage.
|
||||||
* at the end for alignment too.
|
*/
|
||||||
|
class BufferCounter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BufferCounter() = default;
|
||||||
|
~BufferCounter()
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
std::cout << "Remaining buffers on destruction: " << numBuffers << '\n';
|
||||||
|
std::cout << "Total size: " << bytes << '\n';
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void newBuffer(int size)
|
||||||
|
{
|
||||||
|
numBuffers++;
|
||||||
|
bytes.fetch_add(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufferResized(int oldSize, int newSize)
|
||||||
|
{
|
||||||
|
bytes.fetch_add(newSize);
|
||||||
|
bytes.fetch_sub(oldSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufferDeleted(int size)
|
||||||
|
{
|
||||||
|
numBuffers--;
|
||||||
|
bytes.fetch_sub(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getNumBuffers() { return numBuffers; }
|
||||||
|
int getTotalBytes() { return bytes; }
|
||||||
|
private:
|
||||||
|
std::atomic<int> numBuffers { 0 };
|
||||||
|
std::atomic<int> bytes { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A heap buffer structure that tries to align its beginning and
|
||||||
|
* adds a small offset at the end for alignment too.
|
||||||
*
|
*
|
||||||
* Apparently on Linux this effort is mostly useless, and in the end most of the SIMD operations
|
* Apparently on Linux this effort is mostly useless, and in the end
|
||||||
* are coded with alignment checks and sentinels so this class could probably be much simpler.
|
* most of the SIMD operations are coded with alignment checks and
|
||||||
* It does however wrap realloc which in some cases should be a bit more efficient than
|
* sentinels so this class could probably be much simpler. It does
|
||||||
* allocating a whole new block.
|
* however wrap realloc which in some cases should be a bit more
|
||||||
|
* efficient than allocating a whole new block.
|
||||||
*
|
*
|
||||||
* @tparam Type The buffer type
|
* @tparam Type The buffer type
|
||||||
* @tparam Alignment the required alignment in bytes (defaults to SIMDConfig::defaultAlignment)
|
* @tparam Alignment the required alignment in bytes (defaults to
|
||||||
|
* SIMDConfig::defaultAlignment)
|
||||||
*/
|
*/
|
||||||
template <class Type, unsigned int Alignment = SIMDConfig::defaultAlignment>
|
template <class Type, unsigned int Alignment = SIMDConfig::defaultAlignment>
|
||||||
class Buffer {
|
class Buffer {
|
||||||
|
|
@ -64,7 +109,7 @@ public:
|
||||||
*/
|
*/
|
||||||
Buffer()
|
Buffer()
|
||||||
{
|
{
|
||||||
|
counter().newBuffer(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -74,6 +119,7 @@ public:
|
||||||
*/
|
*/
|
||||||
Buffer(size_t size)
|
Buffer(size_t size)
|
||||||
{
|
{
|
||||||
|
counter().newBuffer(0);
|
||||||
resize(size);
|
resize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,6 +144,7 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
counter().bufferResized(largerSize * sizeof(value_type), tempSize * sizeof(value_type));
|
||||||
largerSize = tempSize;
|
largerSize = tempSize;
|
||||||
alignedSize = newSize;
|
alignedSize = newSize;
|
||||||
paddedData = static_cast<pointer>(newData);
|
paddedData = static_cast<pointer>(newData);
|
||||||
|
|
@ -118,6 +165,7 @@ public:
|
||||||
*/
|
*/
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
|
counter().bufferResized(largerSize * sizeof(value_type), 0);
|
||||||
largerSize = 0;
|
largerSize = 0;
|
||||||
alignedSize = 0;
|
alignedSize = 0;
|
||||||
std::free(paddedData);
|
std::free(paddedData);
|
||||||
|
|
@ -127,6 +175,7 @@ public:
|
||||||
}
|
}
|
||||||
~Buffer()
|
~Buffer()
|
||||||
{
|
{
|
||||||
|
counter().bufferDeleted(largerSize * sizeof(value_type));
|
||||||
std::free(paddedData);
|
std::free(paddedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,18 +196,19 @@ public:
|
||||||
*
|
*
|
||||||
* @param other
|
* @param other
|
||||||
*/
|
*/
|
||||||
Buffer(Buffer<Type>&& other)
|
Buffer(Buffer<Type>&& other) = delete;
|
||||||
{
|
// {
|
||||||
if (this != &other) {
|
// if (this != &other) {
|
||||||
std::free(paddedData);
|
// counter().bufferDeleted(largerSize * sizeof(value_type));
|
||||||
largerSize = std::exchange(other.largerSize, 0);
|
// std::free(paddedData);
|
||||||
alignedSize = std::exchange(other.alignedSize, 0);
|
// largerSize = std::exchange(other.largerSize, 0);
|
||||||
paddedData = std::exchange(other.paddedData, nullptr);
|
// alignedSize = std::exchange(other.alignedSize, 0);
|
||||||
normalData = std::exchange(other.normalData, nullptr);
|
// paddedData = std::exchange(other.paddedData, nullptr);
|
||||||
normalEnd = std::exchange(other.normalEnd, nullptr);
|
// normalData = std::exchange(other.normalData, nullptr);
|
||||||
_alignedEnd = std::exchange(other._alignedEnd, nullptr);
|
// normalEnd = std::exchange(other.normalEnd, nullptr);
|
||||||
}
|
// _alignedEnd = std::exchange(other._alignedEnd, nullptr);
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
Buffer<Type>& operator=(const Buffer<Type>& other)
|
Buffer<Type>& operator=(const Buffer<Type>& other)
|
||||||
{
|
{
|
||||||
|
|
@ -169,19 +219,20 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer<Type>& operator=(Buffer<Type>&& other)
|
Buffer<Type>& operator=(Buffer<Type>&& other) = delete;
|
||||||
{
|
// {
|
||||||
if (this != &other) {
|
// if (this != &other) {
|
||||||
std::free(paddedData);
|
// counter().bufferDeleted(largerSize * sizeof(value_type));
|
||||||
largerSize = std::exchange(other.largerSize, 0);
|
// std::free(paddedData);
|
||||||
alignedSize = std::exchange(other.alignedSize, 0);
|
// largerSize = std::exchange(other.largerSize, 0);
|
||||||
paddedData = std::exchange(other.paddedData, nullptr);
|
// alignedSize = std::exchange(other.alignedSize, 0);
|
||||||
normalData = std::exchange(other.normalData, nullptr);
|
// paddedData = std::exchange(other.paddedData, nullptr);
|
||||||
normalEnd = std::exchange(other.normalEnd, nullptr);
|
// normalData = std::exchange(other.normalData, nullptr);
|
||||||
_alignedEnd = std::exchange(other._alignedEnd, nullptr);
|
// normalEnd = std::exchange(other.normalEnd, nullptr);
|
||||||
}
|
// _alignedEnd = std::exchange(other._alignedEnd, nullptr);
|
||||||
return *this;
|
// }
|
||||||
}
|
// return *this;
|
||||||
|
// }
|
||||||
|
|
||||||
Type& operator[](int idx) { return *(normalData + idx); }
|
Type& operator[](int idx) { return *(normalData + idx); }
|
||||||
constexpr pointer data() const noexcept { return normalData; }
|
constexpr pointer data() const noexcept { return normalData; }
|
||||||
|
|
@ -192,6 +243,18 @@ public:
|
||||||
constexpr pointer alignedEnd() noexcept { return _alignedEnd; }
|
constexpr pointer alignedEnd() noexcept { return _alignedEnd; }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the buffer counter object.
|
||||||
|
*
|
||||||
|
* TODO: In C++ 17 all of this can be static inline.
|
||||||
|
*
|
||||||
|
* @return The buffer counter on which you can call various methods.
|
||||||
|
*/
|
||||||
|
static BufferCounter& counter()
|
||||||
|
{
|
||||||
|
static BufferCounter counter;
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
static constexpr auto AlignmentMask { Alignment - 1 };
|
static constexpr auto AlignmentMask { Alignment - 1 };
|
||||||
static constexpr auto TypeAlignment { Alignment / sizeof(value_type) };
|
static constexpr auto TypeAlignment { Alignment / sizeof(value_type) };
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept
|
||||||
{
|
{
|
||||||
// Update all the preloaded sizes
|
// Update all the preloaded sizes
|
||||||
for (auto& preloadedFile : preloadedFiles) {
|
for (auto& preloadedFile : preloadedFiles) {
|
||||||
const auto numFrames = preloadedFile.second.preloadedData->getNumFrames();
|
const auto numFrames = preloadedFile.second.preloadedData->getNumFrames() / oversamplingFactor;
|
||||||
const uint32_t maxOffset = numFrames > this->preloadSize ? numFrames - this->preloadSize : 0;
|
const uint32_t maxOffset = numFrames > this->preloadSize ? numFrames - this->preloadSize : 0;
|
||||||
fs::path file { rootDirectory / std::string(preloadedFile.first) };
|
fs::path file { rootDirectory / std::string(preloadedFile.first) };
|
||||||
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||||
|
|
@ -246,7 +246,7 @@ void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept
|
||||||
{
|
{
|
||||||
float samplerateChange { static_cast<float>(factor) / static_cast<float>(this->oversamplingFactor) };
|
float samplerateChange { static_cast<float>(factor) / static_cast<float>(this->oversamplingFactor) };
|
||||||
for (auto& preloadedFile : preloadedFiles) {
|
for (auto& preloadedFile : preloadedFiles) {
|
||||||
const auto numFrames = preloadedFile.second.preloadedData->getNumFrames();
|
const auto numFrames = preloadedFile.second.preloadedData->getNumFrames() / this->oversamplingFactor;
|
||||||
const uint32_t maxOffset = numFrames > this->preloadSize ? numFrames - this->preloadSize : 0;
|
const uint32_t maxOffset = numFrames > this->preloadSize ? numFrames - this->preloadSize : 0;
|
||||||
fs::path file { rootDirectory / std::string(preloadedFile.first) };
|
fs::path file { rootDirectory / std::string(preloadedFile.first) };
|
||||||
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,20 @@ public:
|
||||||
* @return Oversampling
|
* @return Oversampling
|
||||||
*/
|
*/
|
||||||
uint32_t getPreloadSize() const noexcept;
|
uint32_t getPreloadSize() const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the number of allocated buffers.
|
||||||
|
*
|
||||||
|
* @return The allocated buffers.
|
||||||
|
*/
|
||||||
|
int getAllocatedBuffers() const noexcept { return Buffer<float>::counter().getNumBuffers(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the number of bytes allocated through the buffers
|
||||||
|
*
|
||||||
|
* @return The allocated bytes.
|
||||||
|
*/
|
||||||
|
int getAllocatedBytes() const noexcept { return Buffer<float>::counter().getTotalBytes(); }
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief The parser callback; this is called by the parent object each time
|
* @brief The parser callback; this is called by the parent object each time
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,18 @@ int sfizz_get_num_voices(sfizz_synth_t* synth)
|
||||||
return self->getNumVoices();
|
return self->getNumVoices();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sfizz_get_num_buffers(sfizz_synth_t* synth)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
return self->getAllocatedBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
int sfizz_get_num_bytes(sfizz_synth_t* synth)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
return self->getAllocatedBytes();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -157,8 +157,8 @@ TEST_CASE("[Buffer] Copy and move")
|
||||||
checkBoundaries(copyConstructed, baseSize);
|
checkBoundaries(copyConstructed, baseSize);
|
||||||
REQUIRE(std::all_of(copyConstructed.begin(), copyConstructed.end(), [](auto value) { return value == 1.0f; }));
|
REQUIRE(std::all_of(copyConstructed.begin(), copyConstructed.end(), [](auto value) { return value == 1.0f; }));
|
||||||
|
|
||||||
sfz::Buffer<float> moveConstructed { std::move(buffer) };
|
// sfz::Buffer<float> moveConstructed { std::move(buffer) };
|
||||||
REQUIRE(buffer.empty());
|
// REQUIRE(buffer.empty());
|
||||||
checkBoundaries(moveConstructed, baseSize);
|
// checkBoundaries(moveConstructed, baseSize);
|
||||||
REQUIRE(std::all_of(moveConstructed.begin(), moveConstructed.end(), [](auto value) { return value == 1.0f; }));
|
// REQUIRE(std::all_of(moveConstructed.begin(), moveConstructed.end(), [](auto value) { return value == 1.0f; }));
|
||||||
}
|
}
|
||||||
Loading…
Add table
Reference in a new issue