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/effect/fade_out.hpp>
|
||||||
#include <psemek/audio/constants.hpp>
|
#include <psemek/audio/effect/envelope.hpp>
|
||||||
|
#include <psemek/math/interval.hpp>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace psemek::audio
|
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)
|
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/effect/fade_out.hpp>
|
||||||
#include <psemek/audio/constants.hpp>
|
#include <psemek/audio/effect/envelope.hpp>
|
||||||
|
#include <psemek/math/interval.hpp>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace psemek::audio
|
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)
|
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