monostep/Source/dsp/WaveTables.h

154 lines
3.5 KiB
C
Raw Normal View History

#pragma once
#include <cmath>
#include <algorithm>
2026-08-05 23:45:59 +02:00
#include <cstdint>
2026-08-05 23:12:17 +02:00
#include <random>
namespace monostep
{
enum class Waveform : int
{
sine = 0,
triangle = 1,
saw = 2,
2026-08-05 23:12:17 +02:00
square = 3,
pulse = 4,
noise = 5,
sub = 6,
2026-08-05 23:45:59 +02:00
pluck = 7,
super = 8,
sah = 9,
formant = 10,
pwm = 11
};
2026-08-05 23:45:59 +02:00
static constexpr int numWaveforms = 12;
inline float frac (float x)
{
return x - std::floor (x);
}
inline float polyBlep (float t, float dt)
{
if (t < dt)
{
t /= dt;
return t + t - t * t - 1.0f;
}
if (t > 1.0f - dt)
{
t = (t - 1.0f) / dt;
return t * t + t + t + 1.0f;
}
return 0.0f;
}
inline float renderWave (Waveform w, float phase, float inc)
{
switch (w)
{
case Waveform::sine:
return std::sin (phase * 6.283185307179586f);
case Waveform::triangle:
{
const float f = frac (phase);
return 4.0f * std::min (f, 1.0f - f) - 1.0f;
}
case Waveform::saw:
{
const float f = frac (phase);
return (2.0f * f - 1.0f) - polyBlep (f, inc);
}
case Waveform::square:
{
const float f = frac (phase);
float v = (f < 0.5f) ? 1.0f : -1.0f;
v += polyBlep (f, inc);
v -= polyBlep (frac (f + 0.5f), inc);
return v;
}
2026-08-05 23:12:17 +02:00
case Waveform::pulse:
{
const float f = frac (phase);
return (f < 0.3f) ? 1.0f : -1.0f;
}
case Waveform::sub:
{
const float f = frac (phase * 0.25f);
return 4.0f * std::min (f, 1.0f - f) - 1.0f;
}
case Waveform::noise:
{
thread_local static std::mt19937 rng { std::random_device {}() };
thread_local static std::uniform_real_distribution<float> dist (-1.0f, 1.0f);
return dist (rng);
}
case Waveform::pluck:
{
const float f = frac (phase);
return 2.0f * std::sin (f * 6.283185307179586f * 2.0f) * std::exp (-10.0f * f);
}
2026-08-05 23:45:59 +02:00
case Waveform::super:
{
float sum = 0.0f;
const float detunes[3] = { -0.01f, 0.0f, 0.01f };
for (const float d : detunes)
{
const float f = frac (phase + d);
sum += (2.0f * f - 1.0f) - polyBlep (f, inc);
}
return sum / 3.0f;
}
case Waveform::sah:
{
const int steps = 16;
const float f = frac (phase);
int s = static_cast<int> (std::floor (f * (float) steps));
if (s >= steps) s = steps - 1;
if (s < 0) s = 0;
std::uint32_t h = static_cast<std::uint32_t> (s);
h ^= h << 13; h ^= h >> 17; h ^= h << 5;
const float r = static_cast<float> (h & 0xffffff) / 16777215.0f;
return r * 2.0f - 1.0f;
}
case Waveform::formant:
{
const float w = phase * 6.283185307179586f;
const float env = 0.5f + 0.3f * std::cos (w * 0.5f) + 0.2f * std::cos (w * 0.25f);
return std::sin (w) * env;
}
case Waveform::pwm:
{
const float width = 0.2f;
const float f = frac (phase);
float v = (f < width) ? 1.0f : -1.0f;
v += polyBlep (f, inc);
v -= polyBlep (frac (f + 1.0f - width), inc);
return v;
}
}
return 0.0f;
}
} // namespace monostep