JPL Spatial
Sound spatialization and propagation library
Loading...
Searching...
No Matches
EarlyReflectionsBus.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
28
29#include <atomic>
30#include <cstdint>
31#include <ranges>
32#include <vector>
33#include <span>
34#include <memory_resource>
35#include <memory>
36
37namespace JPL
38{
39 //==================================================================================
45 class ERBus
46 {
47 static constexpr uint32 cScratchSize = 480;
48 public:
49 //==============================================================================
50 // Parameters to create/update early reflection taps
52 {
54 std::vector<float> Gains;
55 float Delay;
57 };
58
59 public:
63
66
73 void ProcessInterleaved(std::span<const float> input, std::span<float> output, uint32 numInputChannels, uint32 numFrames);
74
75 uint32 GetNumChannels() const { return mNumChannels; }
76
77 // Add/remove/update early reflection taps
79 void SetTaps(std::span<const ERUpdateData> newERs);
80
81 //std::atomic<float> RMS{ 0.0f };
82
83 inline uint32 GetNumTaps() const { return mNumTaps; }
84
85 private:
86 //==============================================================================
88
89 enum ERState : int
90 {
91 Stopped = 0,
92 Rendering,
93 FadingOut
94 };
95
96 // Data modified by non-realtime thread
97 struct TargetERData
98 {
99 JPL::simd FilterGains;
100 float* ChannelGains;
101 float DelayTime; // in samples
102 mutable ERState State;
103 };
104
105 // Data modified by realtime thread
106 class RealtimeData final
107 {
108 public:
109 RealtimeData() = default;
110 ~RealtimeData() = default;
111
112 // Non-copyable
113 RealtimeData(const RealtimeData&) = delete;
114 RealtimeData& operator=(const RealtimeData&) = delete;
115
116 public:
117 // Propagation delay
118 TapType Tap;
120
121 // Propagation filtering
122 JPL::simd FilterGains;
124
125 // Panning
126 float* ChannelGains = nullptr;
127
128 // Render state
129 std::atomic<ERState> State;
130 };
131
132 struct ER
133 {
134 TargetERData TargetData; // Non-realtime mutable
135 RealtimeData* RealtimeState; // Realtime mutable
136 uint32 Id; // Immutable
137
138 inline bool operator==(const typename ERBus::ER& other)
139 {
140 return other.Id == Id;
141 }
142
143 inline bool operator==(const typename ERBus::ERUpdateData& other)
144 {
145 return other.Id == Id;
146 }
147 };
148
149 //==============================================================================
150 // Wrapper to control the copy/move/destroy behavior within RealtimeObject
151 // This is effectively a special allocator that holds the ERs.
152 //
153 // RealtimeObject copies the internal object,
154 // and we need a special way to copy data from our pointers,
155 // sinse we're not using vectors for channel gains
156 class ERStorage
157 {
158 public:
159 // Default constructor is deleted
160 // because we always need a memory resource
161 ERStorage() = delete;
162
163 explicit ERStorage(std::pmr::memory_resource& resource);
164 ERStorage(std::pmr::memory_resource& resource, uint32 numChannels);
165
166 ERStorage(const ERStorage& other);
167 ERStorage& operator=(const ERStorage& other);
168
169 ERStorage(ERStorage&& other) noexcept;
170 ERStorage& operator=(ERStorage&& other) noexcept;
171
172 ~ERStorage();
173
174 using AllocatorType = std::pmr::polymorphic_allocator<>;
175 AllocatorType GetAllocator() const { return mAllocator; }
176
177 void UpdateChannelGains(float*& destination, const float* source);
178 float* AllocateCopyChannelGains(const float* source);
179 void DeallocateChannelGains(float* gains);
180
181 const std::pmr::vector<ER>& GetERs() const { return ERs; }
182 std::pmr::vector<ER>& GetERs() { return ERs; }
183
184 ER* FindERByID(uint32 id);
185
186 template<std::ranges::range UpdateData>
187 void PartitionERs(const UpdateData& updateERs, std::span<ER>& outFoundERs, std::span<ER>& outNotFoundERs);
188
189 TargetERData MakeTargetERData(const JPL::simd& filterGains,
190 const float* channelGains,
191 float delayInSamples,
192 ERState state);
193
194 void UpdateTargetERData(TargetERData& er,
195 const JPL::simd& filterGains,
196 const float* channelGains,
197 float delayInSamples,
198 ERState state);
199
200 // Returns number of elements removed
201 template <class Predicate>
202 std::size_t EraseIf(Predicate predicate);
203
204 // Returns number of elements removed
205 template <class Predicate>
206 std::size_t EraseIf(std::size_t begin, std::size_t count, Predicate predicate);
207
208 std::size_t Erase(std::size_t begin, std::size_t count);
209
210 // Returns new ER count
211 std::size_t InsertNewERs(std::pmr::vector<ER>&& newERs);
212
213 private:
214 float* AllocateChannelGains();
215 void AllocateCopyChannelGains(const std::pmr::vector<ER>& source);
216
217 private:
218 std::pmr::polymorphic_allocator<float> mAllocator;
219 std::pmr::vector<ER> ERs;
220 uint32 NumChannels = 0;
221 };
222
224 using SafeERsWrite = typename SafeERs::ScopedAccess<JPL::ThreadType::nonRealtime>;
225 using SafeERsRead = typename SafeERs::ScopedAccess<JPL::ThreadType::realtime>;
226
227 private:
228 RealtimeData* AllocateRTData();
229 RealtimeData* AllocateRTData(const JPL::simd& filterGains, float delayInSamples, ERState state);
230 void DeallocateRTData(RealtimeData* data);
231
232 private:
233 // Memory pool to reduce number of allocations
234 std::pmr::unsynchronized_pool_resource mPool;
235
238
239 SafeERs mERs;
240 uint32 mNumTaps = 0;
241
242 uint32 mNumChannels = 0;
243 float mSampleRate = 48'000.0f;
244 };
245} // namespace JPL
Forward declaration.
Definition DelayLine.h:131
Definition EarlyReflectionsBus.h:46
void SetTaps(std::span< const ERUpdateData > newERs)
Must be called from the same non-audio thread.
ERBus(float sampleRate, uint32 numChannels)
void Prepare(float sampleRate, uint32 numChannels)
Must be called from single non-audio thread.
uint32 GetNumChannels() const
Definition EarlyReflectionsBus.h:75
void ProcessInterleaved(std::span< const float > input, std::span< float > output, uint32 numInputChannels, uint32 numFrames)
uint32 GetNumTaps() const
Definition EarlyReflectionsBus.h:83
Definition RealtimeObject.h:69
Definition AcousticMaterial.h:36
std::uint32_t uint32
Definition Core.h:311
JPL_INLINE constexpr bool operator==(const Vec2 &A, const Vec2 &B) noexcept
Definition MinimalVec2.h:59
JPL_INLINE simd min(const simd &a, const simd &b) noexcept
Element-wise min.
Definition SIMD.h:1813
Definition DelayLine.h:45
Definition EarlyReflectionsBus.h:52
std::vector< float > Gains
Definition EarlyReflectionsBus.h:54
float Delay
Definition EarlyReflectionsBus.h:55
JPL::simd FilterGains
Definition EarlyReflectionsBus.h:53
uint32 Id
Definition EarlyReflectionsBus.h:56
4th order Linkwitz-Riley 4-band crossover
Definition CrossoverFilter.h:417
Definition SmoothedValue.h:29
Minimal 4-wide 32-bit float vector implementation for SIMD.
Definition SIMD.h:60