12#define PDEBUG(...) Ase::debug ("processor", __VA_ARGS__)
18 BusInfo (ident, uilabel,
"",
"", sa), iotag (io_tag)
29 fbuffer_concounter = 0;
38AudioParams::~AudioParams()
60 delete[] old_parameters;
69 count = params.
size();
75 for (
auto [
id,p] : params)
84 for (
auto [
id,p] : params)
85 new_parameters[i++] = p;
86 parameters = new_parameters;
89 values =
new double[count] ();
90 for (
size_t i = 0; i < count; i++)
91 values[i] = parameters[i]->initial().as_double();
93 const size_t u = (count + 64-1) / 64;
98const String AudioProcessor::GUIONLY =
":G:r:w:";
99const String AudioProcessor::STANDARD =
":G:S:r:w:";
100const String AudioProcessor::STORAGEONLY =
":S:r:w:";
101uint64 __thread AudioProcessor::tls_timestamp = 0;
105 engine_ (psetup.engine), aseid_ (psetup.aseid)
107 engine_.processor_count_ += 1;
114 engine_.processor_count_ -= 1;
117 t0events = t0events_.
exchange (t0events);
133 DeviceP devicep = wptr.lock();
138AudioProcessor::zero_buffer()
140 static const FloatBuffer const_zero_float_buffer {};
141 return const_zero_float_buffer;
145AudioProcessor::IOBus&
149 auto &dummy_iobus =
const_cast<IOBus&
> (*
reinterpret_cast<const IOBus*
> (dummy_iobusmem));
150 const size_t busindex =
size_t (obusid) - 1;
152 IOBus &bus = iobuses_[output_offset_ + busindex];
162 auto &dummy_iobus =
const_cast<IOBus&
> (*
reinterpret_cast<const IOBus*
> (dummy_iobusmem));
163 const size_t busindex =
size_t (ibusid) - 1;
165 IOBus &bus = iobuses_[busindex];
172AudioProcessor::release_iobufs()
178 IOBus &bus =
iobus (ob);
179 bus.fbuffer_index = ~0;
180 bus.fbuffer_count = 0;
184 fast_mem_free (fbuffers_);
190AudioProcessor::assign_iobufs ()
195 IOBus &bus =
iobus (ob);
196 bus.fbuffer_index = ochannel_count;
197 bus.fbuffer_count = bus.n_channels();
198 ochannel_count += bus.fbuffer_count;
200 fast_mem_free (fbuffers_);
201 if (ochannel_count > 0)
203 fbuffers_ = (FloatBuffer*) fast_mem_alloc (ochannel_count *
sizeof (FloatBuffer));
204 for (ssize_t i = 0; i < ochannel_count; i++)
205 new (fbuffers_ + i) FloatBuffer();
218 for (
size_t i = 0; i < params_.count; i++)
219 t0events.push_back (make_param_value (params_.ids[i], params_.parameters[i]->initial().as_double()));
230 for (
size_t idx = 0; idx < params_.count; idx++)
231 if (params_.parameters[idx]->cident == ident)
232 return {
ParamId (params_.ids[idx]),
true };
242 if (idx < 0) [[unlikely]]
249 for (
auto &ev : t0events)
250 if (ev.type == MidiEvent::PARAM_VALUE &&
251 ev.param == params_.ids[idx]) {
255 t0events.push_back (make_param_value (params_.ids[idx], v));
265 return idx < 0 ? nullptr : params_.parameters[idx];
275 if (idx < 0) [[unlikely]]
277 return params_.values[idx];
287 return proc->peek_param_mt (paramid);
291AudioProcessor::value_to_normalized (
Id32 paramid,
double value)
const
295 const auto [
fmin,
fmax, step] = parameterp->range();
296 const double normalized = (value -
fmin) / (fmax - fmin);
302AudioProcessor::value_from_normalized (Id32 paramid,
double normalized)
const
304 ParameterC parameterp =
parameter (paramid);
306 const auto [
fmin,
fmax, step] = parameterp->range();
308 assert_return (normalized >= 0.0 && normalized <= 1.0, value);
316 return value_to_normalized (paramid,
get_param (paramid));
327 return send_param (paramid, value_from_normalized (paramid, normalized));
339 return parameterp ? parameterp->value_to_text (value) :
"";
354 return parameterp ? parameterp->value_from_text (text).as_double() : 0.0;
363 if (!parameterp->is_text())
366 cstrings0_.push_back (cstr);
367 return CString::temp_quark_impl (cstr);
374 const CString cstr = CString::temp_quark_impl (vint);
391 assert_return (atomic_bits_ && nth < atomic_bits_->size() * 64,
false);
392 const bool last = atomic_bits_->iter (nth).set (1);
393 const bool need_wakeup = last ==
false;
401 return_unless (atomic_bits_ && pos < atomic_bits_->size() * 64, {});
402 return atomic_bits_->iter (pos);
409 return flags_ & INITIALIZED;
418 const auto [
fmin,
fmax, step] = parameterc->range();
423AudioProcessor::debug_name ()
const
425 return typeid_name (*
this);
441 engine_.schedule_queue_update();
450 engine_.enable_output (*
this, onoff);
461 estreams_->has_event_input =
true;
472 estreams_->has_event_output =
true;
479 if (estreams_ && estreams_->oproc)
483 const bool backlink =
Aux::erase_first (oproc.outputs_, [&] (
auto &e) { return e.proc == this && e.ibusid == EventStreams::EVENT_ISTREAM; });
484 estreams_->oproc =
nullptr;
498 if (estreams_ && estreams_->oproc)
500 estreams_->oproc = &oproc;
502 oproc.outputs_.
push_back ({
this, EventStreams::EVENT_ISTREAM });
522 iobuses_.insert (iobuses_.begin() + output_offset_, bus);
542 iobuses_.push_back (bus);
575AudioProcessor::float_buffer (
IBusId busid,
uint channelindex)
const
577 const size_t ibusindex =
size_t (busid) - 1;
579 const IOBus &ibus =
iobus (busid);
583 const IOBus &obus = oproc.
iobus (ibus.obusid);
585 channelindex = obus.fbuffer_count - 1;
586 return oproc.fbuffers_[obus.fbuffer_index + channelindex];
588 return zero_buffer();
595AudioProcessor::FloatBuffer&
596AudioProcessor::float_buffer (
OBusId obusid,
uint channelindex,
bool resetptr)
598 const size_t obusindex =
size_t (obusid) - 1;
599 FloatBuffer *
const fallback =
const_cast<FloatBuffer*
> (&zero_buffer());
601 const IOBus &obus =
iobus (obusid);
602 assert_return (channelindex < obus.fbuffer_count, *fallback);
603 FloatBuffer &fbuffer = fbuffers_[obus.fbuffer_index + channelindex];
605 fbuffer.buffer = &fbuffer.fblock[0];
613 const size_t obusindex =
size_t (obusid) - 1;
617 FloatBuffer &fbuffer = fbuffers_[obus.fbuffer_index + channelindex];
618 if (block !=
nullptr)
619 fbuffer.buffer =
const_cast<float*
> (block);
629 float *
const buffer =
oblock (b, c);
630 floatfill (buffer, v, AUDIO_BLOCK_MAX_RENDER_SIZE);
639 const size_t obusindex =
size_t (obusid) - 1;
641 return iobus (obusid).fbuffer_concounter;
667 for (
size_t i = 0; i <
n_ibuses(); i++)
678 while (outputs_.
size())
680 const auto o = outputs_.
back();
681 o.proc->disconnect (o.ibusid);
689 if (EventStreams::EVENT_ISTREAM == ibusid)
691 const size_t ibusindex =
size_t (ibusid) - 1;
697 const size_t obusindex =
size_t (ibus.obusid) - 1;
701 obus.fbuffer_concounter -= 1;
702 const bool backlink =
Aux::erase_first (oproc.outputs_, [&] (
auto &e) { return e.proc == this && e.ibusid == ibusid; });
703 ibus.oproc =
nullptr;
715 const size_t ibusindex =
size_t (ibusid) - 1;
717 const size_t obusindex =
size_t (obusid) - 1;
726 (ibus.
speakers == SpeakerArrangement::STEREO &&
730 ibus.obusid = obusid;
732 obus.fbuffer_concounter += 1;
733 oproc.outputs_.
push_back ({
this, ibusid });
741AudioProcessor::ensure_initialized (DeviceP devicep)
749 flags_ |= INITIALIZED;
751 (!estreams_ || (!estreams_->has_event_input && !estreams_->has_event_output)))
752 warning (
"AudioProcessor::%s: initialize() failed to add input/output busses for: %s", __func__, debug_name());
754 reset_state (engine_.frame_counter());
763AudioProcessor::reset_state (
uint64 target_stamp)
765 if (render_stamp_ != target_stamp)
768 estreams_->midi_event_output.clear();
769 reset (target_stamp);
770 render_stamp_ = target_stamp;
776AudioProcessor::reset (
uint64 target_stamp)
784 if (estreams_ && estreams_->oproc)
789 for (
size_t i = 0; i <
n_ibuses(); i++)
798 const uint l = schedule_children();
800 engine_.schedule_add (*
this, level);
818AudioProcessor::render (
uint32 n_frames)
822AudioProcessor::render_block (
uint64 target_stamp)
825 return_unless (target_stamp - render_stamp_ <= AUDIO_BLOCK_MAX_RENDER_SIZE);
828 estreams_->midi_event_output.clear();
829 rc.render_events = t0events_.
exchange (rc.render_events);
830 render_context_ = &rc;
831 render (target_stamp - render_stamp_);
832 render_context_ =
nullptr;
833 render_stamp_ = target_stamp;
834 if (rc.render_events)
836 if (params_.changed) {
837 params_.changed =
false;
843AudioProcessor::MidiEventInput
848 if (estreams_ && estreams_->oproc && estreams_->oproc->estreams_)
849 mev_array[n++] = &estreams_->oproc->estreams_->midi_event_output.vector();
850 if (render_context_->render_events)
851 mev_array[n++] = render_context_->render_events;
862 return estreams_->midi_event_output;
869 AudioProcessor::MakeProcessorP make_shared =
nullptr;
883 entry->
aseid = aseid;
884 entry->static_info = static_info;
885 entry->make_shared = makeproc;
892AudioProcessor::registry_create (
CString aseid,
AudioEngine &engine,
const MakeDeviceP &makedevice)
895 if (entry->aseid == aseid) {
896 AudioProcessorP aproc = entry->make_shared (aseid,
engine);
898 DeviceP devicep = makedevice (aseid, entry->static_info, aproc);
899 aproc->ensure_initialized (devicep);
903 warning (
"AudioProcessor::registry_create: failed to create processor: %s", aseid);
912 fun (entry->aseid, entry->static_info);
918AudioProcessor::FloatBuffer::check ()
920#pragma GCC diagnostic push
921#pragma GCC diagnostic ignored "-Winvalid-offsetof"
923 static_assert (0 == (offsetof (FloatBuffer, fblock[0]) & 63));
924 static_assert (0 == (offsetof (FloatBuffer, canary0_) & 63));
925 static_assert (0 == ((
sizeof (buffer) + offsetof (FloatBuffer, buffer)) & 63));
926#pragma GCC diagnostic pop
939 double inflight_value_ = 0;
946 String hints ()
const {
return parameter_->hints(); }
947 String group ()
const {
return parameter_->group(); }
948 String blurb ()
const {
return parameter_->blurb(); }
949 String descr ()
const {
return parameter_->descr(); }
952 double get_step ()
const override {
const auto [
fmin,
fmax, step] = parameter_->range();
return step; }
955 device_ (devp), parameter_ (parameter), id_ (id)
960 const AudioProcessorP proc = device_->_audio_processor();
961 const double value = inflight_stamp_ > proc->engine().frame_counter() ? inflight_value_ : AudioProcessor::param_peek_mt (proc, id_);
963 vfields[
"value"] = value;
964 this->value.notify();
965 emit_event (
"notify", parameter_->ident(), vfields);
970 set_value (parameter_->initial());
975 const AudioProcessorP proc = device_->_audio_processor();
976 const double value = inflight_stamp_ > proc->engine().frame_counter() ? inflight_value_ : AudioProcessor::param_peek_mt (proc, id_);
977 if (parameter_->is_text())
978 return proc->text_param_from_quark (id_, value);
979 else if (parameter_->is_choice())
980 return proc->param_value_to_text (id_, value);
987 PropertyP thisp = shared_ptr_cast<Property> (
this);
988 const AudioProcessorP proc = device_->_audio_processor();
990 if (parameter_->is_text())
991 v = proc->text_param_to_quark (id_, value.
as_string());
992 else if (value.index() == Value::STRING && parameter_->is_choice())
993 v = proc->param_value_from_text (id_, value.
as_string());
996 proc->send_param (id_, v);
998 inflight_stamp_ = proc->engine().frame_counter();
999 inflight_stamp_ += 2 * proc->engine().block_size();
1000 emit_notify (parameter_->ident());
1006 const AudioProcessorP proc = device_->_audio_processor();
1007 const double value = inflight_stamp_ > proc->engine().frame_counter() ? inflight_value_ : AudioProcessor::param_peek_mt (proc, id_);
1008 const double v = proc->value_to_normalized (id_, value);
1014 const AudioProcessorP proc = device_->_audio_processor();
1015 const auto [
fmin,
fmax, step] = parameter_->range();
1017 return set_value (value);
1022 const AudioProcessorP proc = device_->_audio_processor();
1023 const double value = inflight_stamp_ > proc->engine().frame_counter() ? inflight_value_ : AudioProcessor::param_peek_mt (proc, id_);
1024 return proc->param_value_to_text (id_, value);
1029 const AudioProcessorP proc = device_->_audio_processor();
1030 const double v = proc->param_value_from_text (id_, vstr);
1031 return set_value (v);
1042 return parameter_->choices();
1045 get_metadata ()
const override
1047 return parameter_->metadata();
1055AudioProcessor::access_properties ()
const
1060 props.reserve (params_.count);
1061 for (
size_t idx = 0; idx < params_.count; idx++) {
1062 const uint32_t id = params_.ids[idx];
1063 ParameterC parameterp = params_.parameters[idx];
1065 PropertyP prop = weak_ptr_fetch_or_create<Property> (params_.wprops[idx], [&] () {
1066 return std::make_shared<AudioPropertyImpl> (devp, id, parameterp);
1068 props.push_back (prop);
1074static AudioProcessor *
const enotify_queue_tail = (AudioProcessor*) ptrdiff_t (-1);
1092 expected = enotify_queue_head.
load();
1094 nqueue_next_.
store (expected);
1101AudioProcessor::enotify_dispatch ()
1105 while (head != enotify_queue_tail)
1108 AudioProcessorP procp =
std::exchange (current->nqueue_guard_,
nullptr);
1111 const uint32 nflags = NOTIFYMASK & current->flags_.
fetch_and (~NOTIFYMASK);
1116 if (nflags & BUSCONNECT)
1117 devicep->emit_event (
"bus",
"connect");
1118 if (nflags & BUSDISCONNECT)
1119 devicep->emit_event (
"bus",
"disconnect");
1120 if (nflags & INSERTION)
1121 devicep->emit_event (
"sub",
"insert");
1122 if (nflags & REMOVAL)
1123 devicep->emit_event (
"sub",
"remove");
1124 if (nflags & PARAMCHANGE)
1125 for (
size_t blockoffset = 0; blockoffset < current->params_.count; blockoffset += 64)
1126 if (current->params_.bits[blockoffset >> 6]) {
1128 const size_t bound =
std::min (uint64_t (current->params_.count), blockoffset |
uint64_t (64-1));
1129 for (
size_t idx = blockoffset; idx < bound; idx++)
1130 if (bitmask & uint64_t (1) << (idx & (64-1))) {
1131 PropertyP propi = current->params_.wprops[idx].lock();
1134 aprop->proc_paramchange();
1137 if (nflags & PARAMCHANGE) {
1138 devicep->emit_event (
"params",
"change");
1140 std::swap (current->cstrings0_, current->cstrings1_);
1141 current->cstrings0_.clear();
1149AudioProcessor::enotify_pending ()
1151 return enotify_queue_head != enotify_queue_tail;
T atomic_compare_exchange_strong(T... args)
T atomic_fetch_and(T... args)
Vector of atomic bits, operates in blocks of 64 bits.
Audio signal AudioProcessor base class, implemented by all effects and instruments.
bool set_normalized(Id32 paramid, double normalized)
Set param value normalized into 0…1.
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.
void enotify_enqueue_mt(uint32 pushmask)
void remove_all_buses()
Remove existing bus configurations, useful at the start of configure().
float note_to_freq(int note) const
Convert MIDI note to Hertz according to the current MusicalTuning.
IBusId find_ibus(const String &name) const
Return the IBusId for input bus uilabel or else 0.
double get_param(Id32 paramid)
Fetch value of parameter id.
void prepare_event_input()
IOBus & iobus(OBusId busid)
Get internal output bus handle.
virtual String param_value_to_text(uint32_t paramid, double value) const
bool send_param(Id32 paramid, double value)
Set parameter id to value within ParamInfo.get_minmax().
MidiEventInput midi_event_input()
Access the current MidiEvent inputs during render(), needs prepare_event_input().
MidiEventOutput & midi_event_output()
Access the current output EventStream during render(), needs prepare_event_output().
uint n_ibuses() const
Number of input buses configured for this AudioProcessor.
uint n_ochannels(OBusId busid) const
Number of channels of output bus busid configured for this AudioProcessor.
virtual double param_value_from_text(uint32_t paramid, const String &text) const
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.
bool has_event_input() const
Returns true if this AudioProcessor has an event input stream.
bool is_initialized() const
Check if AudioProcessor has been properly intiialized (so the parameter set is fixed).
void atomic_bits_resize(size_t count)
Prepare count bits for atomic notifications.
void reschedule()
Request recreation of the audio engine rendering schedule.
void prepare_event_output()
AtomicBits::Iter atomic_bits_iter(size_t pos=0) const
Allow iterations over the atomic bits.
MaybeParamId find_param(const String &identifier) const
Return the ParamId for parameter identifier or else 0.
void connect_event_input(AudioProcessor &oproc)
Connect event input to event output of AudioProcessor oproc.
uint text_param_to_quark(uint32_t paramid, const String &text)
Ase main-thread helper for temporary string<->uint conversions.
double get_normalized(Id32 paramid)
Get param value normalized into 0…1.
static void registry_foreach(const std::function< void(const String &aseid, StaticInfo)> &fun)
Iterate over the known AudioProcessor types.
void disconnect_obuses()
Disconnect inputs of all Processors that are connected to outputs of this.
bool atomic_bit_notify(size_t nth)
Set the nth atomic notification bit, return if enotify_enqueue_mt() is needed.
ParameterC parameter(Id32 paramid) const
Retrieve supplemental information for parameters, usually to enhance the user interface.
String text_param_from_quark(uint32_t paramid, uint vint)
Helper for temporary uint<->string conversions.
double peek_param_mt(Id32 paramid) const
AudioEngine & engine() const
Retrieve AudioEngine handle for this AudioProcessor.
void disconnect(IBusId ibus)
Disconnect input ibusid.
virtual void initialize(SpeakerArrangement busses)=0
bool connected(OBusId obusid) const
MinMax param_range(Id32 paramid) const
Retrieve the minimum / maximum values for a parameter.
void enable_engine_output(bool onoff)
Configure if the main output of this module is mixed into the engine output.
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.
virtual ~AudioProcessor()
The destructor is called when the last std::shared_ptr<> reference drops.
uint n_obuses() const
Number of output buses configured for this AudioProcessor.
uint schedule_processor()
Schedule this node and its dependencies for engine rendering.
static double param_peek_mt(const AudioProcessorP proc, Id32 paramid)
bool has_event_output() const
Returns true if this AudioProcessor has an event output stream.
void disconnect_ibuses()
Reset input bus buffer data.
static void registry_add(CString aseid, StaticInfo, MakeProcessorP)
uint n_ichannels(IBusId busid) const
Number of channels of input bus busid configured for this AudioProcessor.
void disconnect_event_input()
Disconnect event input if a connection is present.
void install_params(const AudioParams::Map ¶ms)
Reset list of parameters, enqueues parameter value initializaiton events.
float * oblock(OBusId b, uint c)
OBusId find_obus(const String &name) const
Return the OBusId for output bus uilabel or else 0.
void assign_oblock(OBusId b, uint c, float val)
Fill the output buffer of bus b, channel c with v.
void connect(IBusId ibus, AudioProcessor &oproc, OBusId obus)
Connect input ibusid to output obusid of AudioProcessor prev.
DeviceP get_device() const
Gain access to the Device handle of this AudioProcessor.
String unit() const override
Units of the values within range.
double get_max() const override
Get the maximum property value, converted to double.
void reset() override
Assign default as normalized property value.
bool is_numeric() const override
Whether the property settings can be represented as a floating point number.
double get_step() const override
Get the property value stepping, converted to double.
ChoiceS choices() const override
Enumerate choices for choosable properties.
bool set_value(const Value &value) override
Set the native property value.
Value get_value() const override
Get the native property value.
bool set_text(String vstr) override
Set the current property value as a text String.
bool set_normalized(double normalized) override
Set the normalized property value as double.
String nick() const override
Abbreviated user interface name, usually not more than 6 characters.
double get_min() const override
Get the minimum property value, converted to double.
double get_normalized() const override
Get the normalized property value, converted to double.
String label() const override
Preferred user interface name.
String get_text() const override
Get the current property value, converted to a text String.
String ident() const override
Unique name (per owner) of this Property.
Compact, deduplicating string variant for constant strings.
static CString lookup(const std::string &s)
Implementation type for classes with Event subscription.
A stream of writable MidiEvent structures.
An in-order MidiEvent reader for multiple MidiEvent sources.
A Property allows querying, setting and monitoring of an object property.
T compare_exchange_strong(T... args)
#define ASE_UNLIKELY(expr)
Compiler hint to optimize for expr evaluating to false.
#define ASE_ISLIKELY(expr)
Compiler hint to optimize for expr evaluating to true.
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
#define CLAMP(v, mi, ma)
Yield v clamped to [mi … ma].
#define assert_warn(expr)
Issue an assertion warning if expr evaluates to false.
size_t erase_first(C &container, const std::function< bool(typename C::value_type const &value)> &pred)
Erase first element for which pred() is true in vector or list.
The Anklang C++ API namespace.
String string_to_identifier(const String &input)
Force lower case, alphanumerics + underscore and non-digit start.
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
IBusId
ID type for AudioProcessor input buses, buses are numbered with increasing index.
void floatfill(float *dst, float f, size_t n)
Fill n values of dst with f.
OBusId
ID type for AudioProcessor output buses, buses are numbered with increasing index.
float const_float_zeros[AUDIO_BLOCK_FLOAT_ZEROS_SIZE]
Block of const floats allof value 0.
ParamId
ID type for AudioProcessor parameters, the ID numbers are user assignable.
std::string String
Convenience alias for std::string.
uint32_t uint32
A 32-bit unsigned integer.
uint32_t uint
Provide 'uint' as convenience type.
RtJobQueue main_rt_jobs
Queue a callback for the main_loop without invoking malloc(), addition is obstruction free.
bool string_startswith(const String &string, const String &fragment)
Returns whether string starts with fragment.
Detailed information and common properties of AudioProcessor subclasses.
T shared_from_this(T... args)
void install(const Map ¶ms)
Clear and install a new set of parameters.
ssize_t index(uint32_t id) const
Find index of parameter identified by id or return -1.
CString aseid
Unique identifier for de-/serialization.
uint n_channels() const
Number of channels described by speakers.
SpeakerArrangement speakers
Channel to speaker arrangement.
CString ident
Identifier used for serialization.
Helper class for integer IDs up to 32 Bit, possibly of enum type.
static float note_to_freq(MusicalTuning tuning, int note, float kammer_freq)
Convert MIDI note to Hertz for a MusicalTuning and kammer_freq.
Wrap simple callback pointers, without using malloc (obstruction free).
Value type used to interface with various property types.
double as_double() const
Convert Value to double or return 0.
String as_string() const
Convert Value to a string, not very useful for RECORD or ARRAY.