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:
61 ERBus(float sampleRate, uint32 numChannels);
63
66 void Prepare(float sampleRate, uint32 numChannels);
67
77 void ProcessInterleaved(std::span<const float> input, std::span<float> output, uint32 numInputChannels, uint32 numFrames);
78
79 uint32 GetNumChannels() const { return mNumChannels; }
80
84 void SetTaps(std::span<const ERUpdateData> newERs);
85
86 //std::atomic<float> RMS{ 0.0f };
87
88 inline uint32 GetNumTaps() const { return mNumTaps; }
89
90 private:
91 //==============================================================================
93
94 enum ERState : int
95 {
96 Stopped = 0,
97 Rendering,
98 FadingOut
99 };
100
101 // Data modified by non-realtime thread
102 struct TargetERData
103 {
104 JPL::simd FilterGains;
105 float* ChannelGains;
106 float DelayTime; // in samples
107 mutable ERState State;
108 };
109
110 // Data modified by realtime thread
111 class RealtimeData final
112 {
113 public:
114 RealtimeData() = default;
115 ~RealtimeData() = default;
116
117 // Non-copyable
118 RealtimeData(const RealtimeData&) = delete;
119 RealtimeData& operator=(const RealtimeData&) = delete;
120
121 public:
122 // Propagation delay
123 TapType Tap;
125
126 // Propagation filtering
127 JPL::simd FilterGains;
129
130 // Panning
131 float* ChannelGains = nullptr;
132
133 // Render state
134 std::atomic<ERState> State;
135 };
136
137 struct ER
138 {
139 TargetERData TargetData; // Non-realtime mutable
140 RealtimeData* RealtimeState; // Realtime mutable
141 uint32 Id; // Immutable
142
143 inline bool operator==(const typename ERBus::ER& other)
144 {
145 return other.Id == Id;
146 }
147
148 inline bool operator==(const typename ERBus::ERUpdateData& other)
149 {
150 return other.Id == Id;
151 }
152 };
153
154 //==============================================================================
155 // Wrapper to control the copy/move/destroy behavior within RealtimeObject
156 // This is effectively a special allocator that holds the ERs.
157 //
158 // RealtimeObject copies the internal object,
159 // and we need a special way to copy data from our pointers,
160 // sinse we're not using vectors for channel gains
161 class ERStorage
162 {
163 public:
164 // Default constructor is deleted
165 // because we always need a memory resource
166 ERStorage() = delete;
167
168 explicit ERStorage(std::pmr::memory_resource& resource);
169 ERStorage(std::pmr::memory_resource& resource, uint32 numChannels);
170
171 ERStorage(const ERStorage& other);
172 ERStorage& operator=(const ERStorage& other);
173
174 ERStorage(ERStorage&& other) noexcept;
175 ERStorage& operator=(ERStorage&& other) noexcept;
176
177 ~ERStorage();
178
179 using AllocatorType = std::pmr::polymorphic_allocator<>;
180 AllocatorType GetAllocator() const { return mAllocator; }
181
182 void UpdateChannelGains(float*& destination, const float* source);
183 float* AllocateCopyChannelGains(const float* source);
184 void DeallocateChannelGains(float* gains);
185
186 const std::pmr::vector<ER>& GetERs() const { return ERs; }
187 std::pmr::vector<ER>& GetERs() { return ERs; }
188
189 ER* FindERByID(uint32 id);
190
191 template<std::ranges::range UpdateData>
192 void PartitionERs(const UpdateData& updateERs, std::span<ER>& outFoundERs, std::span<ER>& outNotFoundERs);
193
194 TargetERData MakeTargetERData(const JPL::simd& filterGains,
195 const float* channelGains,
196 float delayInSamples,
197 ERState state);
198
199 void UpdateTargetERData(TargetERData& er,
200 const JPL::simd& filterGains,
201 const float* channelGains,
202 float delayInSamples,
203 ERState state);
204
205 // Returns number of elements removed
206 template <class Predicate>
207 std::size_t EraseIf(Predicate predicate);
208
209 // Returns number of elements removed
210 template <class Predicate>
211 std::size_t EraseIf(std::size_t begin, std::size_t count, Predicate predicate);
212
213 std::size_t Erase(std::size_t begin, std::size_t count);
214
215 // Returns new ER count
216 std::size_t InsertNewERs(std::pmr::vector<ER>&& newERs);
217
218 private:
219 float* AllocateChannelGains();
220 void AllocateCopyChannelGains(const std::pmr::vector<ER>& source);
221
222 private:
223 std::pmr::polymorphic_allocator<float> mAllocator;
224 std::pmr::vector<ER> ERs;
225 uint32 NumChannels = 0;
226 };
227
229 using SafeERsWrite = typename SafeERs::ScopedAccess<JPL::ThreadType::nonRealtime>;
230 using SafeERsRead = typename SafeERs::ScopedAccess<JPL::ThreadType::realtime>;
231
232 private:
233 RealtimeData* AllocateRTData();
234 RealtimeData* AllocateRTData(const JPL::simd& filterGains, float delayInSamples, ERState state);
235 void DeallocateRTData(RealtimeData* data);
236
237 private:
238 // Memory pool to reduce number of allocations
239 std::pmr::unsynchronized_pool_resource mPool;
240
243
244 SafeERs mERs;
245 uint32 mNumTaps = 0;
246
247 uint32 mNumChannels = 0;
248 float mSampleRate = 48'000.0f;
249 };
250} // namespace JPL
Forward declaration.
Definition DelayLine.h:131
Definition EarlyReflectionsBus.h:46
void SetTaps(std::span< const ERUpdateData > newERs)
ERBus(float sampleRate, uint32 numChannels)
void Prepare(float sampleRate, uint32 numChannels)
uint32 GetNumChannels() const
Definition EarlyReflectionsBus.h:79
void ProcessInterleaved(std::span< const float > input, std::span< float > output, uint32 numInputChannels, uint32 numFrames)
uint32 GetNumTaps() const
Definition EarlyReflectionsBus.h:88
Definition RealtimeObject.h:69
Definition AcousticMaterial.h:36
std::uint32_t uint32
Definition Core.h:311
std::unique_ptr< T, PmrDeleter< T > > pmr_unique_ptr
Alias for unique ptr using pmr allocator and deleter.
Definition Memory.h:233
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