Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
combo.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "combo.hh"
3#include "server.hh"
4#include "internal.hh"
5
6#define PDEBUG(...) Ase::debug ("combo", __VA_ARGS__)
7
8namespace Ase {
9
10static constexpr OBusId OUT1 = OBusId (1);
11
12// == Inlet ==
14 AudioChain &audio_chain_;
15public:
16 Inlet (const ProcessorSetup &psetup, AudioChain *audiochain) :
17 AudioProcessor (psetup),
18 audio_chain_ (*audiochain)
19 {
20 assert_return (nullptr != audiochain);
21 }
22 void reset (uint64 target_stamp) override {}
23 void
25 {
27 auto output = add_output_bus ("Output", audio_chain_.ispeakers_);
28 (void) output;
29 }
30 void
31 render (uint n_frames) override
32 {
33 const IBusId i1 = IBusId (1);
34 const OBusId o1 = OBusId (1);
35 const uint ni = audio_chain_.n_ichannels (i1);
36 const uint no = this->n_ochannels (o1);
37 assert_return (ni == no);
38 for (size_t i = 0; i < ni; i++)
39 redirect_oblock (o1, i, audio_chain_.ifloats (i1, i));
40 }
41};
42
43// == AudioCombo ==
44AudioCombo::AudioCombo (const ProcessorSetup &psetup) :
45 AudioProcessor (psetup)
46{}
47
48AudioCombo::~AudioCombo ()
49{
50 eproc_ = nullptr;
51}
52
55void
56AudioCombo::insert (AudioProcessorP proc, ssize_t pos)
57{
58 assert_return (proc != nullptr);
59 if (pos == -1)
60 pos = processors_.size(); // insert at the end of the chain
61 const size_t index = CLAMP (pos, 0, processors_.size());
62 processors_.insert (processors_.begin() + index, proc);
63 // fixup following connections
64 reconnect (index, true);
65 reschedule();
66 enotify_enqueue_mt (INSERTION);
67}
68
70bool
72{
74 AudioProcessorP processorp;
75 size_t pos; // find proc
76 for (pos = 0; pos < processors_.size(); pos++)
77 if (processors_[pos].get() == &proc)
78 {
79 processorp = processors_[pos]; // and remove...
80 processors_.erase (processors_.begin() + pos);
81 break;
82 }
83 if (!processorp)
84 return false;
85 // clear stale connections
86 pm_disconnect_ibuses (*processorp);
87 pm_disconnect_obuses (*processorp);
88 // fixup following connections
89 reconnect (pos, false);
90 enotify_enqueue_mt (REMOVAL);
91 reschedule();
92 return true;
93}
94
96AudioProcessorP
98{
99 return_unless (nth < size(), {});
100 return processors_[nth];
101}
102
106{
107 for (size_t i = 0; i < processors_.size(); i++)
108 if (processors_[i].get() == &proc)
109 return i;
110 return ~size_t (0);
111}
112
114size_t
116{
117 return processors_.size();
118}
119
121AudioProcessorS
123{
124 return processors_; // copy
125}
126
128void
129AudioCombo::set_event_source (AudioProcessorP eproc)
130{
131 if (eproc)
132 assert_return (eproc->has_event_output());
133 eproc_ = eproc;
134}
135
136// == AudioChain ==
137AudioChain::AudioChain (const ProcessorSetup &psetup, SpeakerArrangement iobuses) :
138 AudioCombo (psetup),
139 ispeakers_ (iobuses), ospeakers_ (iobuses)
140{
141 assert_return (speaker_arrangement_count_channels (iobuses) > 0);
142 inlet_ = AudioProcessor::create_processor<AudioChain::Inlet> (engine_, this);
143 assert_return (inlet_ != nullptr);
144 probe_block_ = SERVER->telemem_allocate (sizeof (ProbeArray));
145 probes_ = new (probe_block_.block_start) ProbeArray{};
146}
147
148AudioChain::~AudioChain()
149{
150 pm_remove_all_buses (*inlet_);
151 inlet_ = nullptr;
152 probes_->~ProbeArray();
153 probes_ = nullptr;
154 SERVER->telemem_release (probe_block_);
155}
156
157void
158AudioChain::static_info (AudioProcessorInfo &info)
159{} // avoid public listing
160
161void
163{
164 auto ibus = add_input_bus ("Input", ispeakers_);
165 auto obus = add_output_bus ("Output", ospeakers_);
166 (void) ibus;
167 assert_return (OUT1 == obus);
168}
169
170void
172{}
173
174uint
175AudioChain::schedule_children()
176{
177 last_output_ = nullptr;
178 uint level = schedule_processor (*inlet_);
179 for (auto procp : processors_)
180 {
181 const uint l = schedule_processor (*procp);
182 level = std::max (level, l);
183 if (procp->n_obuses())
184 last_output_ = procp.get();
185 }
186 // last_output_ is only valid during render()
187 return level;
188}
189
190void
192{
193 // make the last processor output the chain output
194 const size_t nlastchannels = last_output_ ? last_output_->n_ochannels (OUT1) : 0;
195 const size_t n_och = n_ochannels (OUT1);
196 std::array<Probe,2> *probes = n_och <= 2 ? probes_ : nullptr;
197 for (size_t c = 0; c < n_och; c++)
198 {
199 // an enqueue_children() call is guranteed *before* render(), so last_output_ is valid
200 if (UNLIKELY (!last_output_))
201 {
202 redirect_oblock (OUT1, c, nullptr);
203 if (probes)
204 (*probes)[c].dbspl = -192;
205 }
206 else
207 {
208 const float *cblock = last_output_->ofloats (OUT1, std::min (c, nlastchannels - 1));
209 redirect_oblock (OUT1, c, cblock);
210 if (probes)
211 {
212 // SPL = 20 * log10 (root_mean_square (p) / p0) dB ; https://en.wikipedia.org/wiki/Sound_pressure#Sound_pressure_level
213 // const float sqrsig = square_sum (n_frames, cblock) / n_frames; // * 1.0 / p0^2
214 const float sqrsig = square_max (n_frames, cblock);
215 const float log2div = 3.01029995663981; // 20 / log2 (10) / 2.0
216 const float db_spl = ISLIKELY (sqrsig > 0.0) ? log2div * fast_log2 (sqrsig) : -192;
217 (*probes)[c].dbspl = db_spl;
218 }
219 }
220 }
221 // TODO: do we need to assign oblock when no children are present?
222}
223
225void
226AudioChain::reconnect (size_t index, bool insertion)
227{
228 // clear stale inputs
229 for (size_t i = index; i < processors_.size(); i++)
230 pm_disconnect_ibuses (*processors_[i]);
231 // reconnect pairwise
232 for (size_t i = index; i < processors_.size(); i++)
233 chain_up (*(i ? processors_[i - 1] : inlet_), *processors_[i]);
234}
235
237uint
239{
240 assert_return (this != &prev, 0);
241 assert_return (this != &next, 0);
242 const uint ni = next.n_ibuses();
243 const uint no = prev.n_obuses();
244 // assign event source
245 if (eproc_)
246 {
247 if (prev.has_event_input())
248 pm_connect_event_input (*eproc_, prev);
249 if (next.has_event_input())
250 pm_connect_event_input (*eproc_, next);
251 }
252 // check need for audio connections
253 if (ni == 0 || no == 0)
254 return 0; // nothing to do
255 uint n_connected = 0;
256 // try to connect prev main obus (1) with next main ibus (1)
257 const OBusId obusid { 1 };
258 const IBusId ibusid { 1 };
259 SpeakerArrangement ospa = speaker_arrangement_channels (prev.bus_info (obusid).speakers);
260 SpeakerArrangement ispa = speaker_arrangement_channels (next.bus_info (ibusid).speakers);
261 if (0 == (uint64_t (ispa) & ~uint64_t (ospa)) || // exact match
262 (ospa == SpeakerArrangement::MONO && // allow MONO -> STEREO connections
263 ispa == SpeakerArrangement::STEREO))
264 {
265 n_connected += speaker_arrangement_count_channels (ispa);
266 pm_connect (next, ibusid, prev, obusid);
267 }
268 return n_connected;
269}
270
272AudioChain::run_probes (bool enable)
273{
274 if (enable && !probes_enabled_)
275 for (size_t i = 0; i < probes_->size(); i++)
276 (*probes_)[i] = Probe{};
277 probes_enabled_ = enable;
278 return probes_enabled_ ? probes_ : nullptr;
279}
280
281static const auto audio_chain_id = register_audio_processor<AudioChain>();
282
283} // Ase
void initialize(SpeakerArrangement busses) override
Definition combo.cc:24
void reset(uint64 target_stamp) override
Reset all state variables.
Definition combo.cc:22
void render(uint n_frames) override
Definition combo.cc:31
void render(uint n_frames) override
Definition combo.cc:191
void initialize(SpeakerArrangement busses) override
Definition combo.cc:162
uint chain_up(AudioProcessor &pfirst, AudioProcessor &psecond)
Connect the main audio input of next to audio output of prev.
Definition combo.cc:238
void reconnect(size_t index, bool insertion) override
Reconnect AudioChain child processors at start and after.
Definition combo.cc:226
void reset(uint64 target_stamp) override
Reset all state variables.
Definition combo.cc:171
bool remove(AudioProcessor &proc)
Remove a previously added AudioProcessor proc from the AudioCombo.
Definition combo.cc:71
AudioProcessorS list_processors() const
Retrieve list of AudioProcessorS contained in this AudioCombo.
Definition combo.cc:122
void insert(AudioProcessorP proc, ssize_t pos=~size_t(0))
Definition combo.cc:56
AudioProcessorP at(uint nth)
Return the AudioProcessor at position nth in the AudioCombo.
Definition combo.cc:97
void set_event_source(AudioProcessorP eproc)
Assign event source for future auto-connections of chld processors.
Definition combo.cc:129
size_t size()
Return the number of AudioProcessor instances in the AudioCombo.
Definition combo.cc:115
ssize_t find_pos(AudioProcessor &proc)
Return the index of AudioProcessor proc in the AudioCombo.
Definition combo.cc:105
Audio signal AudioProcessor base class, implemented by all effects and instruments.
Definition processor.hh:76
void redirect_oblock(OBusId b, uint c, const float *block)
Redirect output buffer of bus b, channel c to point to block, or zeros if block==nullptr.
Definition processor.cc:611
void enotify_enqueue_mt(uint32 pushmask)
void remove_all_buses()
Remove existing bus configurations, useful at the start of configure().
Definition processor.cc:646
const float * ifloats(IBusId b, uint c) const
Access readonly float buffer of input bus b, channel c, see also ofloats().
Definition processor.hh:546
uint n_ochannels(OBusId busid) const
Number of channels of output bus busid configured for this AudioProcessor.
Definition processor.hh:509
OBusId add_output_bus(CString uilabel, SpeakerArrangement speakerarrangement, const String &hints="", const String &blurb="")
Add an output bus with uilabel and channels configured via speakerarrangement.
Definition processor.cc:530
const float * ofloats(OBusId b, uint c) const
Access readonly float buffer of output bus b, channel c, see also oblock().
Definition processor.hh:553
void reschedule()
Request recreation of the audio engine rendering schedule.
Definition processor.cc:439
IBusId add_input_bus(CString uilabel, SpeakerArrangement speakerarrangement, const String &hints="", const String &blurb="")
Add an input bus with uilabel and channels configured via speakerarrangement.
Definition processor.cc:510
uint schedule_processor()
Schedule this node and its dependencies for engine rendering.
Definition processor.cc:781
uint n_ichannels(IBusId busid) const
Number of channels of input bus busid configured for this AudioProcessor.
Definition processor.hh:501
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:71
#define CLAMP(v, mi, ma)
Yield v clamped to [mi … ma].
Definition internal.hh:58
#define UNLIKELY(cond)
Hint to the compiler to optimize for cond == FALSE.
Definition internal.hh:63
#define ISLIKELY(cond)
Hint to the compiler to optimize for cond == TRUE.
Definition internal.hh:61
T max(T... args)
T min(T... args)
The Anklang C++ API namespace.
Definition api.hh:9
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:25
IBusId
ID type for AudioProcessor input buses, buses are numbered with increasing index.
Definition processor.hh:21
SpeakerArrangement
Definition transport.hh:11
@ MONO
Single Channel (M)
OBusId
ID type for AudioProcessor output buses, buses are numbered with increasing index.
Definition processor.hh:25
float square_max(uint n_values, const float *ivalues)
Find the maximum suqared value in a block of floats.
Definition datautils.cc:19
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:18
float fast_log2(float x)
Definition mathutils.hh:101
T size(T... args)
typedef uint64_t
typedef ssize_t