Skip to content

Commit

Permalink
Add AirWindowsVinylDither
Browse files Browse the repository at this point in the history
  • Loading branch information
tobiashienzsch committed Feb 10, 2024
1 parent 7dd337f commit 042ccbc
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 2 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ if(NOT CMAKE_CROSSCOMPILING)
"lib/grit/audio/dynamic/transient_shaper_test.cpp"
"lib/grit/audio/envelope/envelope_follower_test.cpp"
"lib/grit/audio/music/note_test.cpp"
"lib/grit/audio/noise/airwindows_vinyl_dither_test.cpp"
"lib/grit/audio/noise/dither_test.cpp"
"lib/grit/audio/noise/white_noise_test.cpp"
"lib/grit/audio/waveshape/full_wave_rectifier_test.cpp"
Expand Down
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ target_sources(grit-eurorack INTERFACE
"grit/audio/music/note.hpp"

"grit/audio/noise.hpp"
"grit/audio/noise/airwindows_vinyl_dither.hpp"
"grit/audio/noise/dither.hpp"
"grit/audio/noise/white_noise.hpp"

Expand Down
3 changes: 2 additions & 1 deletion lib/grit/audio/noise.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once

#include <grit/audio/noise/airwindows_vinyl_dither.hpp>
#include <grit/audio/noise/dither.hpp>
#include <grit/audio/noise/white_noise.hpp>
#include <grit/audio/noise/white_noise.hpp>
124 changes: 124 additions & 0 deletions lib/grit/audio/noise/airwindows_vinyl_dither.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#pragma once

#include <etl/algorithm.hpp>
#include <etl/array.hpp>
#include <etl/cmath.hpp>
#include <etl/concepts.hpp>
#include <etl/random.hpp>

namespace grit {

template<etl::floating_point Float, typename URNG = etl::xoshiro128plusplus>
struct AirWindowsVinylDither
{
using value_type = Float;
using seed_type = typename URNG::result_type;

AirWindowsVinylDither() = default;
explicit AirWindowsVinylDither(seed_type seed);

auto setDeRez(Float deRez) -> void;
[[nodiscard]] auto getDeRez() const -> Float;

[[nodiscard]] auto operator()(Float x) -> Float;
auto reset() -> void;

private:
auto advanceNoise() -> Float;

Float _deRez{0};
Float _inScale{0};
Float _outScale{0};

URNG _rng{42};
etl::uniform_real_distribution<Float> _dist{Float(-0.5), Float(0.5)};

Float _nsOdd{0};
Float _prev{0};
etl::array<Float, 16> _ns{};
};

template<etl::floating_point Float, typename URNG>
AirWindowsVinylDither<Float, URNG>::AirWindowsVinylDither(seed_type seed) : _rng{seed}
{}

template<etl::floating_point Float, typename URNG>
auto AirWindowsVinylDither<Float, URNG>::setDeRez(Float deRez) -> void
{
_deRez = deRez;

auto scaleFactor = Float(32768.0);
if (deRez > Float(0)) {
scaleFactor *= etl::pow(Float(1) - deRez, Float(6));
}
if (scaleFactor < Float(0.0001)) {
scaleFactor = Float(0.0001);
}

auto outScale = scaleFactor;
if (outScale < Float(8)) {
outScale = Float(8);
}

_inScale = scaleFactor;
_outScale = Float(1) / outScale;
}

template<etl::floating_point Float, typename URNG>
auto AirWindowsVinylDither<Float, URNG>::getDeRez() const -> Float
{
return _deRez;
}

template<etl::floating_point Float, typename URNG>
auto AirWindowsVinylDither<Float, URNG>::operator()(Float x) -> Float
{
auto const y = x * _inScale;

auto absSample = advanceNoise();
absSample += y;

if (_nsOdd > 0) {
_nsOdd -= Float(0.97);
}
if (_nsOdd < 0) {
_nsOdd += Float(0.97);
}

_nsOdd -= (_nsOdd * _nsOdd * _nsOdd * Float(0.475));
_nsOdd += _prev;

absSample += (_nsOdd * Float(0.475));
auto const floor = etl::floor(absSample);

_prev = floor - y;
return floor * _outScale;
}

template<etl::floating_point Float, typename URNG>
auto AirWindowsVinylDither<Float, URNG>::reset() -> void
{
_nsOdd = Float(0);
_prev = Float(0);
etl::fill(_ns.begin(), _ns.end(), Float(0));
}

template<etl::floating_point Float, typename URNG>
auto AirWindowsVinylDither<Float, URNG>::advanceNoise() -> Float
{
auto absSample = _dist(_rng);
_ns[0] += absSample;
_ns[0] *= Float(0.5);
absSample -= _ns[0];

for (auto i{1U}; i < _ns.size(); ++i) {
absSample += _dist(_rng);
_ns[i] += absSample;
_ns[i] *= Float(0.5);
absSample -= _ns[i];
}

return absSample;
}

} // namespace grit
19 changes: 19 additions & 0 deletions lib/grit/audio/noise/airwindows_vinyl_dither_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include "airwindows_vinyl_dither.hpp"

#include <catch2/catch_approx.hpp>
#include <catch2/catch_template_test_macros.hpp>

TEMPLATE_TEST_CASE("grit/audio/noise: AirWindowsVinylDither", "", float, double)
{
using Float = TestType;

auto proc = grit::AirWindowsVinylDither<Float>{42};
proc.reset();
REQUIRE(proc(Float(0.0)) == Catch::Approx(Float(0.0)));

proc.setDeRez(Float(0.5));
REQUIRE(proc.getDeRez() == Catch::Approx(Float(0.5)));
REQUIRE(proc(Float(0.125)) != Float(0.0));
REQUIRE(proc(Float(0.111)) != Float(0.0));
REQUIRE(proc(Float(0.113)) != Float(0.0));
}
2 changes: 1 addition & 1 deletion lib/grit/audio/noise/dither_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <random>

TEMPLATE_PRODUCT_TEST_CASE(
"grit/audio/dither:",
"grit/audio/noise: Dither",
"",
(grit::NoDither, grit::RectangleDither, grit::TriangleDither),
(etl::xorshift32, etl::xoshiro128plus, std::mt19937)
Expand Down

0 comments on commit 042ccbc

Please sign in to comment.