3#include <ossia/network/value/value.hpp>
4#include <ossia/math/safe_math.hpp>
30 static constexpr std::size_t maxFrameSize = 65536;
31 static constexpr std::size_t maxFrames = 256;
32 static constexpr std::size_t maxSamples = 1048576;
34 std::size_t frameSize{};
35 std::vector<float> samples;
37 std::size_t frameCount()
const noexcept
39 return frameSize ? samples.size() / frameSize : 0;
42 std::span<const float> frame(std::size_t index)
const noexcept
44 assert(index < frameCount());
45 return {samples.data() + index * frameSize, frameSize};
48 bool valid()
const noexcept
50 return frameSize >= 2 && frameSize <= maxFrameSize
51 && !samples.empty() && samples.size() <= maxSamples
52 && samples.size() % frameSize == 0 && frameCount() <= maxFrames
53 && std::all_of(samples.begin(), samples.end(), [](
float value) {
54 return ossia::safe_isfinite(value) && value >= -1.f && value <= 1.f;
63inline std::optional<float>
waveformNumber(
const ossia::value& value)
noexcept
66 if(
const auto* real = value.target<
float>())
68 else if(
const auto* integer = value.target<
int>())
69 number = float(*integer);
70 else if(
const auto* flag = value.target<
bool>())
71 number = *flag ? 1.f : 0.f;
74 if(!ossia::safe_isfinite(number))
80inline std::optional<float>
waveformSample(
const ossia::value& value)
noexcept
83 return std::clamp(*number, -1.f, 1.f);
92 const auto* frames = value.target<std::vector<ossia::value>>();
93 if(!frames || frames->empty() || frames->size() > WavetableData::maxFrames)
95 const auto* first = frames->front().target<std::vector<ossia::value>>();
96 if(!first || first->size() < 2 || first->size() > WavetableData::maxFrameSize
97 || frames->size() > WavetableData::maxSamples / first->size())
101 result.frameSize = first->size();
102 result.samples.reserve(result.frameSize * frames->size());
103 for(
const auto& frameValue : *frames)
105 const auto* frame = frameValue.target<std::vector<ossia::value>>();
106 if(!frame || frame->size() != result.frameSize)
108 for(
const auto& sample : *frame)
111 if(!amplitude || *amplitude < -1.f || *amplitude > 1.f)
113 result.samples.push_back(*amplitude);
119inline ossia::value wavetableToValue(
const WavetableData& table)
122 throw std::invalid_argument(
"Invalid periodic sample table");
123 std::vector<ossia::value> frames;
124 frames.reserve(table.frameCount());
125 for(std::size_t index = 0; index < table.frameCount(); ++index)
127 std::vector<ossia::value> samples;
128 samples.reserve(table.frameSize);
129 for(
float sample : table.frame(index))
130 samples.emplace_back(sample);
131 frames.emplace_back(std::move(samples));
138inline bool isWaveformFrame(std::span<const ossia::value> samples)
noexcept
140 return samples.size() >= 2 && samples.size() <= WavetableData::maxFrameSize
141 && std::all_of(samples.begin(), samples.end(), [](
const ossia::value& v) {
142 return bool(waveformSample(v));
153inline std::optional<std::span<const ossia::value>>
156 const auto* list = value.target<std::vector<ossia::value>>();
157 if(!list || list->empty())
159 std::span<const ossia::value> samples;
160 if(
const auto* nested = list->front().target<std::vector<ossia::value>>(); nested)
162 if(frame >= list->size())
164 const auto* selected = (*list)[frame].target<std::vector<ossia::value>>();
175 if(!detail::isWaveformFrame(samples))
185 const auto* list = value.target<std::vector<ossia::value>>();
186 if(!list || list->empty())
188 const bool bank = bool(list->front().target<std::vector<ossia::value>>());
189 const std::size_t frames = bank ? list->size() : 1;
190 if(frames > WavetableData::maxFrames)
193 for(std::size_t index = 0; index < frames; ++index)
196 if(!samples || (index > 0 && samples->size() != result.frameSize)
197 || frames > WavetableData::maxSamples / samples->size())
199 result.frameSize = samples->size();
200 for(
const auto& sample : *samples)
206inline WavetableData makeTriangleWavetable(std::size_t frameSize = 32)
208 if(frameSize < 2 || frameSize > WavetableData::maxFrameSize)
209 throw std::invalid_argument(
"Invalid waveform resolution");
210 WavetableData table{frameSize, std::vector<float>(frameSize)};
211 for(std::size_t sample = 0; sample < frameSize; ++sample)
213 const double phase = double(sample) / frameSize;
214 table.samples[sample] = float(1. - 4. * std::abs(phase - .5));
223 value = ossia::safe_isfinite(value) ? std::clamp(value, -1., 1.) : 0.;
225 value = 2. * std::round((value + 1.) * .5 * (levels - 1)) / (levels - 1) - 1.;
226 return std::clamp(
float(value), -1.f, 1.f);
234 return int(std::lround((std::clamp(value, -1.f, 1.f) + 1.) * .5 * (levels - 1)));
237enum class WaveformShape
252 std::span<float> frame, WaveformShape shape,
int levels, std::uint32_t seed = 1) noexcept
254 const std::size_t size = frame.size();
257 std::uint32_t state = seed ? seed : 0x9E3779B9u;
258 for(std::size_t i = 0; i < size; ++i)
260 const double phase = double(i) / size;
261 const double ramp = size > 1 ? double(i) / (size - 1) : 0.;
265 case WaveformShape::Sine:
266 value = std::sin(2. * 3.14159265358979323846 * phase);
268 case WaveformShape::Triangle:
269 value = 1. - 4. * std::abs(phase - .5);
271 case WaveformShape::SawUp:
272 value = 2. * ramp - 1.;
274 case WaveformShape::SawDown:
275 value = 1. - 2. * ramp;
277 case WaveformShape::Square:
278 value = phase < .5 ? 1. : -1.;
280 case WaveformShape::Pulse25:
281 value = phase < .25 ? 1. : -1.;
283 case WaveformShape::Pulse12:
284 value = phase < .125 ? 1. : -1.;
286 case WaveformShape::Noise:
288 state ^= state << 13;
289 state ^= state >> 17;
291 value = 2. * (double(state) / 4294967295.) - 1.;
303 for(
float sample : frame)
304 peak = std::max(peak, std::abs(sample));
307 for(
float& sample : frame)
311inline void invertWaveform(std::span<float> frame,
int levels)
noexcept
313 for(
float& sample : frame)
317inline void reverseWaveform(std::span<float> frame)
noexcept
319 std::reverse(frame.begin(), frame.end());
328 std::rotate(frame.begin(), frame.begin() + 1, frame.end());
330 std::rotate(frame.rbegin(), frame.rbegin() + 1, frame.rend());
336 const std::size_t size = frame.size();
339 const std::vector<float> source(frame.begin(), frame.end());
340 for(std::size_t i = 0; i < size; ++i)
342 const double previous = source[(i + size - 1) % size];
343 const double next = source[(i + 1) % size];
344 frame[i] =
quantizeSample((previous + 2. * source[i] + next) * .25, levels);
351 std::fill(frame.begin(), frame.end(),
quantizeSample(0., levels));
357 const WavetableData& table, std::size_t frame,
double phase)
noexcept
359 assert(table.frameSize >= 2 && frame < table.frameCount() && ossia::safe_isfinite(phase));
360 phase -= std::floor(phase);
361 const double position = phase * table.frameSize;
362 const auto left = std::min(std::size_t(position), table.frameSize - 1);
363 const auto right = (left + 1) % table.frameSize;
364 const auto samples = table.frame(frame);
365 return float(samples[left] + (samples[right] - samples[left]) * (position - left));
372 WavetableData result{size, std::vector<float>(source.frameCount() * size)};
373 for(std::size_t frame = 0; frame < source.frameCount(); ++frame)
374 for(std::size_t sample = 0; sample < size; ++sample)
375 result.samples[frame * size + sample]
382template <
typename T, std::
size_t N,
typename Sample>
383bool resampleWaveform(
384 std::size_t size, Sample&& sample, std::array<T, N>& destination,
double low,
385 double high)
noexcept
387 static_assert(N > 0 && std::is_arithmetic_v<T>);
388 if(size < 2 || !ossia::safe_isfinite(low) || !ossia::safe_isfinite(high) || low >= high
389 || low < double(std::numeric_limits<T>::lowest())
390 || high > double(std::numeric_limits<T>::max()))
392 std::array<T, N> prepared;
393 for(std::size_t index = 0; index < N; ++index)
395 const double position = (double(index) / N) * size;
396 const auto left = std::min(std::size_t(position), size - 1);
397 const auto right = (left + 1) % size;
398 const float a = sample(left), b = sample(right);
399 const double interpolated = float(a + (b - a) * (position - left));
400 const double mapped = low + (high - low) * ((interpolated + 1.) * .5);
401 if constexpr(std::is_integral_v<T>)
402 prepared[index] = T(std::round(mapped));
404 prepared[index] = T(mapped);
406 destination = prepared;
415template <
typename T, std::
size_t N>
418 double low,
double high, std::size_t frame = 0) noexcept
420 if(!table.valid() || frame >= table.frameCount())
422 const auto samples = table.frame(frame);
423 return detail::resampleWaveform(
424 samples.size(), [samples](std::size_t i) { return samples[i]; }, destination, low,
430template <
typename T, std::
size_t N>
432 const ossia::value& value, std::array<T, N>& destination,
double low,
433 double high, std::size_t frame = 0) noexcept
438 return detail::resampleWaveform(
440 [samples](std::size_t i) { return *waveformSample((*samples)[i]); },
441 destination, low, high);
Base classes and tools to implement processes and layers.
Definition JSONVisitor.hpp:1115
std::optional< WavetableData > wavetableFromValue(const ossia::value &value)
Definition Wavetable.hpp:90
WavetableData resampleWavetable(const WavetableData &source, std::size_t size)
Definition Wavetable.hpp:370
void clearWaveform(std::span< float > frame, int levels) noexcept
Sets every sample to the level nearest to zero.
Definition Wavetable.hpp:349
void rotateWaveform(std::span< float > frame, bool left) noexcept
Moves every sample one position earlier (left) or later, wrapping.
Definition Wavetable.hpp:323
std::optional< WavetableData > waveformFromValue(const ossia::value &value)
Definition Wavetable.hpp:183
std::optional< std::span< const ossia::value > > waveformFrame(const ossia::value &value, std::size_t frame=0) noexcept
Definition Wavetable.hpp:154
void generateWaveform(std::span< float > frame, WaveformShape shape, int levels, std::uint32_t seed=1) noexcept
Definition Wavetable.hpp:251
void smoothWaveform(std::span< float > frame, int levels)
One pass of a periodic [1 2 1] / 4 low-pass.
Definition Wavetable.hpp:334
void normalizeWaveform(std::span< float > frame, int levels) noexcept
Definition Wavetable.hpp:300
std::optional< float > waveformSample(const ossia::value &value) noexcept
A sample of a waveform value: a waveformNumber clamped to [-1, 1].
Definition Wavetable.hpp:80
float quantizeSample(double value, int levels) noexcept
Definition Wavetable.hpp:221
float sampleWavetable(const WavetableData &table, std::size_t frame, double phase) noexcept
Definition Wavetable.hpp:356
int sampleLevel(float value, int levels) noexcept
Definition Wavetable.hpp:231
std::optional< float > waveformNumber(const ossia::value &value) noexcept
Definition Wavetable.hpp:63
bool prepareWaveform(const WavetableData &table, std::array< T, N > &destination, double low, double high, std::size_t frame=0) noexcept
Definition Wavetable.hpp:416
Definition Wavetable.hpp:29