diff --git a/libs/audio/include/psemek/audio/wave/karplus_strong.hpp b/libs/audio/include/psemek/audio/wave/karplus_strong.hpp new file mode 100644 index 00000000..1e9f277c --- /dev/null +++ b/libs/audio/include/psemek/audio/wave/karplus_strong.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace psemek::audio +{ + + stream_ptr karplus_strong(float frequency); + +} diff --git a/libs/audio/source/wave/karplus_strong.cpp b/libs/audio/source/wave/karplus_strong.cpp new file mode 100644 index 00000000..4313786d --- /dev/null +++ b/libs/audio/source/wave/karplus_strong.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include + +#include +#include + +namespace psemek::audio +{ + + namespace + { + + struct karplus_strong_impl + : stream + { + karplus_strong_impl(float frequency) + : buffer_(2 * std::round(audio::frequency / frequency)) + { + random::generator rng{0x4b0a763ef6573bf2ull, 0}; + for (auto & v : buffer_) + v = random::uniform(rng, -1.f, 1.f); + } + + std::optional length() const override + { + return std::nullopt; + } + + std::size_t read(float * data, std::size_t sample_count) override + { + std::size_t const size = buffer_.size(); + + for (std::size_t i = 0; i < sample_count; i += 2) + { + buffer_[buffer_pos_ + 0] = (buffer_[buffer_pos_ + 0] + buffer_[(buffer_pos_ + 2) % size + 0]) / 2.f; + buffer_[buffer_pos_ + 1] = (buffer_[buffer_pos_ + 1] + buffer_[(buffer_pos_ + 2) % size + 1]) / 2.f; + + data[i + 0] = buffer_[buffer_pos_ + 0]; + data[i + 1] = buffer_[buffer_pos_ + 1]; + + buffer_pos_ += 2; + buffer_pos_ %= size; + } + + return sample_count; + } + + std::size_t played() const override + { + return played_.load(); + } + + private: + std::vector buffer_; + std::size_t buffer_pos_{0}; + + std::atomic played_; + }; + + } + + stream_ptr karplus_strong(float frequency) + { + return std::make_shared(frequency); + } + +}