Add generic envelope filter and re-implement fade in/out using it
This commit is contained in:
parent
dbf5117dc6
commit
c177911737
3 changed files with 78 additions and 127 deletions
67
libs/audio/include/psemek/audio/effect/envelope.hpp
Normal file
67
libs/audio/include/psemek/audio/effect/envelope.hpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#include <psemek/audio/stream.hpp>
|
||||
#include <psemek/audio/duration.hpp>
|
||||
|
||||
namespace psemek::audio
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <typename EnvelopeFn>
|
||||
struct envelope_impl
|
||||
: stream
|
||||
{
|
||||
envelope_impl(stream_ptr stream, EnvelopeFn envelope_fn, std::optional<duration> truncate)
|
||||
: stream_(std::move(stream))
|
||||
, envelope_fn_(std::move(envelope_fn))
|
||||
, remaining_(truncate ? std::optional{truncate->samples()} : std::nullopt)
|
||||
, total_length_(truncate ? stream_->played() + *remaining_ : stream_->length())
|
||||
{}
|
||||
|
||||
std::optional<std::size_t> length() const override
|
||||
{
|
||||
return total_length_;
|
||||
}
|
||||
|
||||
std::size_t read(util::span<float> samples) override
|
||||
{
|
||||
std::size_t const max_count = remaining_ ? std::min(*remaining_, samples.size()) : samples.size();
|
||||
|
||||
auto const count = stream_->read(samples.prefix(max_count));
|
||||
if (remaining_)
|
||||
*remaining_ -= count;
|
||||
|
||||
auto out = samples.data();
|
||||
for (std::size_t i = 0; i < count; i += 2)
|
||||
{
|
||||
float gain = envelope_fn_(played_);
|
||||
out[0] *= gain;
|
||||
out[1] *= gain;
|
||||
out += 2;
|
||||
played_ += duration::from_frames(1);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
std::size_t played() const override
|
||||
{
|
||||
return stream_->played();
|
||||
}
|
||||
|
||||
private:
|
||||
stream_ptr stream_;
|
||||
EnvelopeFn envelope_fn_;
|
||||
duration played_;
|
||||
std::optional<std::size_t> remaining_;
|
||||
std::optional<std::size_t> total_length_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename EnvelopeFn>
|
||||
stream_ptr envelope(stream_ptr stream, EnvelopeFn envelope_fn, std::optional<duration> truncate = {})
|
||||
{
|
||||
return std::make_shared<envelope_impl<EnvelopeFn>>(std::move(stream), std::move(envelope_fn), truncate);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +1,15 @@
|
|||
#include <psemek/audio/effect/fade_in.hpp>
|
||||
#include <psemek/audio/constants.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <psemek/audio/effect/fade_out.hpp>
|
||||
#include <psemek/audio/effect/envelope.hpp>
|
||||
#include <psemek/math/interval.hpp>
|
||||
|
||||
namespace psemek::audio
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct fade_in_impl
|
||||
: stream
|
||||
{
|
||||
fade_in_impl(stream_ptr stream, duration length, duration start)
|
||||
: stream_(std::move(stream))
|
||||
, length_(length)
|
||||
, start_(start)
|
||||
{}
|
||||
|
||||
std::optional<std::size_t> length() const override
|
||||
{
|
||||
return stream_->length();
|
||||
}
|
||||
|
||||
std::size_t played() const override
|
||||
{
|
||||
return stream_->played();
|
||||
}
|
||||
|
||||
std::size_t read(util::span<float> samples) override
|
||||
{
|
||||
auto const count = stream_->read(samples);
|
||||
std::fill(samples.begin(), samples.begin() + std::min<std::size_t>(count, start_.samples()), 0.f);
|
||||
|
||||
if (count <= start_.samples())
|
||||
{
|
||||
start_ -= count;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::size_t i = start_.samples(); i < count; i += 2)
|
||||
{
|
||||
float m = static_cast<float>(std::min<std::size_t>(current_, length_.samples())) / length_.samples();
|
||||
|
||||
samples[i + 0] *= m;
|
||||
samples[i + 1] *= m;
|
||||
|
||||
current_ += 2;
|
||||
}
|
||||
start_ = duration{};
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private:
|
||||
stream_ptr stream_;
|
||||
duration length_;
|
||||
duration start_;
|
||||
std::size_t current_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
stream_ptr fade_in(stream_ptr stream, duration length, duration start)
|
||||
{
|
||||
return std::make_shared<fade_in_impl>(std::move(stream), length, start);
|
||||
return envelope(std::move(stream), [start, length](duration time){
|
||||
return math::clamp(static_cast<float>(time.frames() - start.frames()) / length.frames(), {0.f, 1.f});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +1,15 @@
|
|||
#include <psemek/audio/effect/fade_out.hpp>
|
||||
#include <psemek/audio/constants.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <psemek/audio/effect/envelope.hpp>
|
||||
#include <psemek/math/interval.hpp>
|
||||
|
||||
namespace psemek::audio
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct fade_out_impl
|
||||
: stream
|
||||
{
|
||||
fade_out_impl(stream_ptr stream, duration length, duration start)
|
||||
: stream_(std::move(stream))
|
||||
, length_(length)
|
||||
, start_(start)
|
||||
, total_length_(stream_->played() + start_.samples() + length_.samples())
|
||||
{}
|
||||
|
||||
std::optional<std::size_t> length() const override
|
||||
{
|
||||
return total_length_;
|
||||
}
|
||||
|
||||
std::size_t played() const override
|
||||
{
|
||||
return stream_->played();
|
||||
}
|
||||
|
||||
std::size_t read(util::span<float> samples) override
|
||||
{
|
||||
if (current_ >= length_.samples())
|
||||
return 0;
|
||||
|
||||
auto result = stream_->read(samples);
|
||||
|
||||
if (result <= start_.samples())
|
||||
{
|
||||
start_ -= duration::from_samples(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::size_t i = start_.samples(); i < result; i += 2)
|
||||
{
|
||||
float m = static_cast<float>(length_.samples() - std::min<std::size_t>(current_, length_.samples())) / length_.samples();
|
||||
|
||||
samples[i + 0] *= m;
|
||||
samples[i + 1] *= m;
|
||||
|
||||
current_ += 2;
|
||||
}
|
||||
start_ = {};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
stream_ptr stream_;
|
||||
duration length_;
|
||||
duration start_;
|
||||
std::size_t current_ = 0;
|
||||
std::size_t total_length_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
stream_ptr fade_out(stream_ptr stream, duration length, duration start)
|
||||
{
|
||||
return std::make_shared<fade_out_impl>(std::move(stream), length, start);
|
||||
return envelope(std::move(stream), [start, length](duration time){
|
||||
return math::clamp(static_cast<float>(length.frames() + start.frames() - time.frames()) / length.frames(), {0.f, 1.f});
|
||||
}, start + length);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue