JPL Spatial
Sound spatialization and propagation library
Loading...
Searching...
No Matches
DelayLine.h
Go to the documentation of this file.
1//
2// ██╗██████╗ ██╗ ██╗██████╗ ███████╗
3// ██║██╔══██╗ ██║ ██║██╔══██╗██╔════╝ ** JPLSpatial **
4// ██║██████╔╝ ██║ ██║██████╔╝███████╗
5// ██ ██║██╔═══╝ ██║ ██║██╔══██╗╚════██║ https://github.com/Jaytheway/JPLSpatial
6// ╚█████╔╝██║ ███████╗██║██████╔╝███████║
7// ╚════╝ ╚═╝ ╚══════╝╚═╝╚═════╝ ╚══════╝
8//
9// Copyright Jaroslav Pevno, JPLSpatial is offered under the terms of the ISC license:
10//
11// Permission to use, copy, modify, and/or distribute this software for any purpose with or
12// without fee is hereby granted, provided that the above copyright notice and this permission
13// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
14// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
16// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
17// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#pragma once
21
25
26#include <algorithm>
27#include <cmath>
28#include <cstdint>
29#include <numbers>
30#include <vector>
31#include <span>
32
33namespace JPL
34{
35 //==========================================================================
37 template<uint32_t MaxWindow>
38 class DelayLine;
39
40 //==========================================================================
43 template<class InterpolatorT = Thiran1stInterpolator>
44 struct DelayTap
45 {
46 public:
48
49 int DelaySample = 0; // whole-sample part
51
53 inline void SetDelay(float delay)
54 {
55 JPL_ASSERT(delay >= 0.0f && "Delay must be non-negative");
56 DelaySample = static_cast<uint32_t>(delay);
57 const float delayFraction = delay - DelaySample;
58 Interpolator.SetFraction(delayFraction);
59 }
60
62 template<class DelayLine> requires(DelayLine::WindowSize >= InterpolatorType::InputLength)
63 inline float Process(const DelayLine& delayLine)
64 {
66 return Interpolator.Process(data);
67 }
68 };
69
70 //==========================================================================
74 {
75 static constexpr uint32_t InputLength = 1;
76
78
81 inline void SetCrossfade(float alpha)
82 {
83 mGainA = 0.5f * (1.0f + cosf(std::numbers::pi_v<float> * alpha));
84 mGainB = 1.0f - mGainA;
85 }
86
89 inline void SetDelay(float delay)
90 {
91 JPL_ASSERT(delay >= 0.0f && "Delay must be non-negative");
92
94 // since we crossfade smoothely
95 // we may not need to interpolate samples
96 mLastDelay = mNewDelay;
97 mNewDelay = static_cast<int>(delay);
98
99 // TODO: if we were in the middle of a crossfade,
100 // do we want to swap the gains and "continue"
101 // crossfading action into the new delay time?
102 // std::swap(mGainA, mGainB);
103 // ..fade out from mGainA current value...
104 //
105 // OR: avoid setting new target delay in the middle of a crossfade!
106 }
107
109 template<class DelayLine> requires(DelayLine::WindowSize >= InputLength)
110 inline float Process(const DelayLine& delayLine)
111 {
112 const float tapA = delayLine.template GetReadWindow<InputLength>(mLastDelay);
113 const float tapB = delayLine.template GetReadWindow<InputLength>(mNewDelay);
114 return tapA * mGainA + tapB * mGainB;
115 }
116
117 private:
118 int mLastDelay = 0;
119 int mNewDelay = 0;
120 float mGainA = 1.0f;
121 float mGainB = 0.0f;
122 };
123
124 //==========================================================================
129 template<uint32_t MaxWindow = 2>
131 {
132 public:
133 static constexpr uint32_t WindowSize = MaxWindow;
134
135 private:
137 std::vector<float> mBuffer; // size = Ring + WindowSize
138
139 public:
140 // @param minSize: minimum required size of the delay in samples,
141 // the actual lengh of the delay buffer is going to be next power of 2
143 : mIndex(minSize)
144 , mBuffer(mIndex.GetTotalSize(), 0.0f)
145 {
146 }
147
148 DelayLine() : mIndex(0) {}
149
151 {
153 mBuffer.resize(mIndex.GetTotalSize(), 0.0f);
154 }
155
156 template<class TapType> requires (TapType::InterpolatorType::InputLength <= WindowSize)
157 static TapType CreateTap(float initialDelay = 0.0f)
158 {
159 TapType tap;
160 tap.SetDelay(initialDelay);
161 return tap;
162 }
163
164 template<class Interpolator> requires (Interpolator::InputLength <= WindowSize)
166 {
168 tap.SetDelay(initialDelay);
169 return tap;
170 }
171
172 inline uint32_t GetSize() const { return mIndex.GetRingSize(); }
173 inline uint32_t GetWriteIndex() const { return mIndex.GetCurrent().WriteIndex; }
174 inline const float* raw() const { return mBuffer.data(); }
175
176 inline void Clear()
177 {
178 std::fill(mBuffer.begin(), mBuffer.end(), 0.0f);
179 mIndex.Reset();
180 }
181
182 // Push one live sample into the ring buffer
183 inline void Push(float sample)
184 {
185 if constexpr (WindowSize > 1)
186 {
187 const auto [writeIndex, mirrorIndex] = mIndex--;
188 // Write into ring and mirror
189 mBuffer[writeIndex] = sample;
190 mBuffer[mirrorIndex] = sample;
191 }
192 else
193 {
194 const auto writeIndex = mIndex--;
195 mBuffer[writeIndex] = sample;
196 }
197 }
198
199 // Return span to a contiguous data window that start `intDelay` samples
200 // The returned data is guaranteed to be of length `max(WindowLength, 1)`
201 template<uint32_t WindowLength> requires (WindowLength > 1 && WindowLength <= WindowSize)
202 inline std::span<const float, WindowLength> GetReadWindow(uint32_t intDelay) const
203 {
204#if 0 // Ring index writing forward
205 // start of contiguous `MaxWindow` window at an offset from the WriteIndex
207#else
208 // +1 because offset is from the current write position,
209 // while with delay 0 we need last written sample
210 const uint32_t readIndex = mIndex.GetOffset(intDelay + 1);
211#endif
212 return std::span<const float, WindowLength>(&mBuffer[readIndex], WindowLength);
213 }
214
215 // Return sample at `intDelay`
216 template<uint32_t WindowLength> requires (WindowLength <= 1)
217 inline float GetReadWindow(uint32_t intDelay) const
218 {
219#if 0 // Ring index writing forward
220 static constexpr uint32_t windowLength = 1;
221 // start of contiguous `MaxWindow` window at an offset from the WriteIndex
223#else
224 // +1 because offset is from the current write position,
225 // while with delay 0 we need last written sample
226 const uint32_t readIndex = mIndex.GetOffset(intDelay + 1);
227#endif
228 return mBuffer[readIndex];
229 }
230 };
231
232} // namespace JPL
#define JPL_ASSERT(inExpression,...)
Main assert macro, usage: JPL_ASSERT(condition, message) or JPL_ASSERT(condition)
Definition ErrorReporting.h:76
Forward declaration.
Definition AbstractIndex.h:83
uint32_t GetRingSize() const
Definition AbstractIndex.h:106
void Reset()
Definition AbstractIndex.h:108
uint32_t GetOffset(uint32_t offset) const
Get the index of the sample at an offset from the WriteIndex.
Definition AbstractIndex.h:141
Index GetCurrent() const
Definition AbstractIndex.h:107
uint32_t GetTotalSize() const
Get total size of the ring buffer including MaxWindow mirror length.
Definition AbstractIndex.h:105
Forward declaration.
Definition DelayLine.h:131
uint32_t GetSize() const
Definition DelayLine.h:172
void Resize(uint32_t newSize)
Definition DelayLine.h:150
DelayLine()
Definition DelayLine.h:148
uint32_t GetWriteIndex() const
Definition DelayLine.h:173
std::span< const float, WindowLength > GetReadWindow(uint32_t intDelay) const
Definition DelayLine.h:202
void Clear()
Definition DelayLine.h:176
static constexpr uint32_t WindowSize
Definition DelayLine.h:133
void Push(float sample)
Definition DelayLine.h:183
float GetReadWindow(uint32_t intDelay) const
Definition DelayLine.h:217
static DelayTap< Interpolator > CreateTap(float initialDelay=0.0f)
Definition DelayLine.h:165
const float * raw() const
Definition DelayLine.h:174
static TapType CreateTap(float initialDelay=0.0f)
Definition DelayLine.h:157
DelayLine(uint32_t minSize)
Definition DelayLine.h:142
Definition AcousticMaterial.h:36
JPL_INLINE simd min(const simd &a, const simd &b) noexcept
Element-wise min.
Definition SIMD.h:1813
uint32_t WriteIndex
Definition AbstractIndex.h:90
Definition DelayLine.h:74
float Process(const DelayLine &delayLine)
Process delay crossfade and get the delayed sample.
Definition DelayLine.h:110
void SetDelay(float delay)
Definition DelayLine.h:89
void SetCrossfade(float alpha)
Definition DelayLine.h:81
static constexpr uint32_t InputLength
Definition DelayLine.h:75
Definition DelayLine.h:45
InterpolatorType Interpolator
Definition DelayLine.h:50
InterpolatorT InterpolatorType
Definition DelayLine.h:47
int DelaySample
Definition DelayLine.h:49
float Process(const DelayLine &delayLine)
Process delay interpolation and get the delayed sample.
Definition DelayLine.h:63
void SetDelay(float delay)
Set delay is in samples, can be fractional.
Definition DelayLine.h:53