Skip to content

Commit

Permalink
Merge branch 'param-events'
Browse files Browse the repository at this point in the history
* param-events:
  DEVICES: blepsynth/blepsynth.cc: port to new Parameter API
	* Start using install_params()
	* Rename pid_key_[abcdefg]_, add key prefix
	* Declare all parameter IDs in an enum
	* Use ase_gettext() multi arg formatting
	* Construct AudioProcessor with ProcessorSetup&
	* Add "toggle" hint for piano key properties
	* Handle PARAM_VALUE events via apply_event()
	* Start using midi_event_input()
	* Port remaining OSC1 and OSC2 parameter references
	* Adjust and check parameter state *after* processing MIDI events
	* Adjust parameter ID types
  DEVICES: freeverb/freeverb.cc: support new param events
	* Use install_params(ParameterMap)
	* Construct AudioProcessor with ProcessorSetup&
	* Use adjust_all_params() and apply_input_events()
	* Fix MODE hints and param position
  DEVICES: colorednoise.cc: use adjust_all_params() and apply_input_events()
	* Port to install_params(ParameterMap)
	* Construct AudioProcessor with ProcessorSetup&
  DEVICES: Makefile.mk: include colorednoise.cc in the build
  ASE: midilib.cc: use midi_event_input() and midi_event_output()
  ASE: clapplugin.cc: use midi_event_input()
  ASE: engine: allow atomic frame_counter and block_size access
  ASE: engine.cc: use midi_event_output()
  ASE: midievent: rename + rewrite MidiEventReader to read many queues
	* Base MidiEventReader on QueueMultiplexer<> to turn
	  it into a multiplexing event queue reader
	* Rename MidiEventReader (former MidiEventRange)
  ASE: clapplugin.cc: use call_delete<>()
  ASE: cxxaux.hh: add call_delete<> to quickly create delete callbacks
  ASE: rename MidiEventOutput (former MidiEventStream)
  ASE: nativedevice.cc: use AudioProcessor.access_properties()
  ASE: midilib: construct AudioProcessor with ProcessorSetup&
  ASE: api.hh, server.cc: add Server.engine_stats()
  ASE: engine: add AudioEngine.engine_stats()
  ASE: clapplugin.cc: construct AudioProcessor with ProcessorSetup&
  ASE: clapplugin: remove unused flags fiels from params
  ASE: combo: construct AudioProcessor with ProcessorSetup&
  ASE: processor: implement parameter change events
	* Introduce AudioParams, t0events, install_params(), apply_event()
	* Implement AudioParams::dirty()
	* Implement AudioProcessor.access_properties()
	* Store aseid_ in each AudioProcessor
	* Add property weak pointers to AudioParams
	* Support install_params() with an ordinary map<>
	* Initialize parameter cache with intial value
	* Disable Property inflight handling code
	* Rename MidiEventOutput (former MidiEventStream)
	* Implement multiplexing MIDI event handling
	* Atomically modify and swap t0events to pass between threads
	* Leave new/delete of t0events to the main-thread
	* Add modify_t0events<>() helper to care about atomic swapping
	* Rename and rewrite get_event_input() and get_event_output()
	* Introduce RenderContext for rendering specific variables
	* midi_event_input(): setup multiplexing event queue reader
	* midi_event_output(): provide writable MIDI event vector
	* Implement send_param()
	* Only set dirty and changed for params with property object
	* Enqueue PARAMCHANGE notification if params changed
	* Simplify event processing
	* Provide apply_input_events() to assign PARAM_CHANGE events
	* Use uint32_t as parameter id type for processors
	* Provide adjust_all_params() to call adjust_param() for all params
  ASE: midievent: support MidiMessage::PARAM_VALUE
  ASE: midievent: add PARAM_VALUE and make_param_value()
  ASE: parameter: add ParameterMap and support groups
	* Make `ident` the first Param() field, adjust callers
	* Add ParameterMap helper type
	* Support Param.MinMaxStep as double min,max pair
	* Catch simple cases of duplicate IDs, ident, label
	* Assign group to new ParameterMap Params
  ASE: queuemux: support iterators and assign via std::array<const Queue*>
  ASE: queuemux: add QueueMultiplexer.assign() and support iterator template arg

Signed-off-by: Tim Janik <[email protected]>
  • Loading branch information
tim-janik committed Nov 16, 2023
2 parents 89cc7d6 + 8e1185c commit 8f23a2d
Show file tree
Hide file tree
Showing 31 changed files with 885 additions and 753 deletions.
1 change: 1 addition & 0 deletions ase/api.hh
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ public:
int32 interval_ms) = 0; ///< Broadcast telemetry memory segments to the current Jsonipc connection.
virtual StringS list_preferences () = 0; ///< Retrieve a list of all preference identifiers.
virtual PropertyP access_preference (const String &ident) = 0; ///< Retrieve property handle for a Preference identifier.
String engine_stats (); ///< Print engine state.
// projects
virtual ProjectP last_project () = 0; ///< Retrieve the last created project.
virtual ProjectP create_project (String projectname) = 0; ///< Create a new project (name is modified to be unique if necessary.
Expand Down
29 changes: 10 additions & 19 deletions ase/clapplugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ struct ClapParamInfoImpl : ClapParamInfo {
{
unset();
param_id = cinfo.id;
flags = cinfo.flags;
ident = string_format ("%08x", param_id);
name = cinfo.name;
module = cinfo.module;
Expand All @@ -159,7 +158,6 @@ void
ClapParamInfo::unset()
{
param_id = CLAP_INVALID_ID;
flags = 0;
ident = "";
name = "";
module = "";
Expand Down Expand Up @@ -238,15 +236,15 @@ class ClapAudioProcessor : public AudioProcessor {
{
info.label = "Anklang.Devices.ClapAudioProcessor";
}
ClapAudioProcessor (AudioEngine &engine) :
AudioProcessor (engine)
ClapAudioProcessor (const ProcessorSetup &psetup) :
AudioProcessor (psetup)
{}
~ClapAudioProcessor()
{
while (enqueued_events_.size()) {
ClapEventParamS *pevents = enqueued_events_.back();
enqueued_events_.pop_back();
main_rt_jobs += RtCall (delete_clap_event_params, pevents); // delete in main_thread
main_rt_jobs += RtCall (call_delete<ClapEventParamS>, pevents); // delete in main_thread
}
}
void
Expand Down Expand Up @@ -491,16 +489,10 @@ class ClapAudioProcessor : public AudioProcessor {
enqueued_events_.erase (enqueued_events_.begin());
for (const auto &e : *pevents)
need_wakeup |= apply_param_value_event (e);
main_rt_jobs += RtCall (delete_clap_event_params, pevents); // delete in main_thread
main_rt_jobs += RtCall (call_delete<ClapEventParamS>, pevents); // delete in main_thread
}
return need_wakeup;
}
static void
delete_clap_event_params (ClapEventParamS *p)
{
assert_return (this_thread_is_ase());
delete p;
}
};
static CString clap_audio_wrapper_aseid = register_audio_processor<ClapAudioProcessor>();

Expand Down Expand Up @@ -546,12 +538,12 @@ setup_expression (ClapEventUnion *evunion, uint32_t time, uint16_t port_index)
void
ClapAudioProcessor::convert_clap_events (const clap_process_t &process, const bool as_clapnotes)
{
MidiEventRange erange = get_event_input();
if (input_events_.capacity() < erange.events_pending())
input_events_.reserve (erange.events_pending() + 128);
input_events_.resize (erange.events_pending());
MidiEventInput evinput = midi_event_input();
if (input_events_.capacity() < evinput.events_pending())
input_events_.reserve (evinput.events_pending() + 128);
input_events_.resize (evinput.events_pending());
uint j = 0;
for (const auto &ev : erange)
for (const auto &ev : evinput)
switch (ev.message())
{
clap_event_note_expression *expr;
Expand Down Expand Up @@ -798,7 +790,6 @@ class ClapPluginHandleImpl : public ClapPluginHandle {
ClapParamUpdate update = {
.steady_time = 0, // NOW
.param_id = param_id,
.flags = 0,
.value = CLAMP (v, info->min_value, info->max_value),
};
updates.push_back (update);
Expand All @@ -818,7 +809,7 @@ class ClapPluginHandleImpl : public ClapPluginHandle {
loader_updates_ = new ClapParamUpdateS;
for (const auto &[id, value] : params)
loader_updates_->push_back ({
.steady_time = 0, .param_id = id, .flags = 0, .value = value,
.steady_time = 0, .param_id = id, .value = value,
});
// TODO: flush loader_updates_ right away
}
Expand Down
1 change: 0 additions & 1 deletion ase/clapplugin.hh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public:
struct ClapParamUpdate {
int64_t steady_time = 0; // unimplemented
clap_id param_id = CLAP_INVALID_ID;
uint32_t flags = 0;
double value = NAN;
};
using ClapParamUpdateS = std::vector<ClapParamUpdate>;
Expand Down
12 changes: 6 additions & 6 deletions ase/combo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ static constexpr OBusId OUT1 = OBusId (1);
class AudioChain::Inlet : public AudioProcessor {
AudioChain &audio_chain_;
public:
Inlet (AudioEngine &engine, AudioChain *audiochain) :
AudioProcessor (engine),
Inlet (const ProcessorSetup &psetup, AudioChain *audiochain) :
AudioProcessor (psetup),
audio_chain_ (*audiochain)
{
assert_return (nullptr != audiochain);
Expand All @@ -42,8 +42,8 @@ class AudioChain::Inlet : public AudioProcessor {
};

// == AudioCombo ==
AudioCombo::AudioCombo (AudioEngine &engine) :
AudioProcessor (engine)
AudioCombo::AudioCombo (const ProcessorSetup &psetup) :
AudioProcessor (psetup)
{}

AudioCombo::~AudioCombo ()
Expand Down Expand Up @@ -133,8 +133,8 @@ AudioCombo::set_event_source (AudioProcessorP eproc)
}

// == AudioChain ==
AudioChain::AudioChain (AudioEngine &engine, SpeakerArrangement iobuses) :
AudioCombo (engine),
AudioChain::AudioChain (const ProcessorSetup &psetup, SpeakerArrangement iobuses) :
AudioCombo (psetup),
ispeakers_ (iobuses), ospeakers_ (iobuses)
{
assert_return (speaker_arrangement_count_channels (iobuses) > 0);
Expand Down
4 changes: 2 additions & 2 deletions ase/combo.hh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ protected:
AudioProcessorS processors_;
AudioProcessorP eproc_;
virtual void reconnect (size_t index, bool insertion) = 0;
explicit AudioCombo (AudioEngine &engine);
explicit AudioCombo (const ProcessorSetup&);
virtual ~AudioCombo ();
public:
void insert (AudioProcessorP proc, ssize_t pos = ~size_t (0));
Expand All @@ -37,7 +37,7 @@ protected:
void reconnect (size_t index, bool insertion) override;
uint chain_up (AudioProcessor &pfirst, AudioProcessor &psecond);
public:
explicit AudioChain (AudioEngine &engine, SpeakerArrangement iobuses = SpeakerArrangement::STEREO);
explicit AudioChain (const ProcessorSetup&, SpeakerArrangement iobuses = SpeakerArrangement::STEREO);
virtual ~AudioChain ();
struct Probe { float dbspl = -192; };
using ProbeArray = std::array<Probe,2>;
Expand Down
7 changes: 7 additions & 0 deletions ase/cxxaux.hh
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@ delete_inplace (Type &typemem)
typemem.~Type();
}

/// Simple way to create a standalone callback to delete an object of type `T`.
template<class T> void
call_delete (T *o)
{
delete o; // automatically handles nullptr
}

/// REQUIRES<value> - Simplified version of std::enable_if<cond,bool>::type to use SFINAE in function templates.
template<bool value> using REQUIRES = typename ::std::enable_if<value, bool>::type;

Expand Down
4 changes: 2 additions & 2 deletions ase/driver-alsa.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1136,7 +1136,7 @@ class AlsaSeqMidiDriver : public MidiDriver {
return snd_seq_event_input_pending (seq_, pull_fifo) > 0;
}
uint
fetch_events (MidiEventStream &estream, double samplerate) override
fetch_events (MidiEventOutput &estream, double samplerate) override
{
assert_return (!!evparser_, 0);
const size_t old_size = estream.size();
Expand All @@ -1147,7 +1147,7 @@ class AlsaSeqMidiDriver : public MidiDriver {
return (channel + 1) * 128 + note;
};
bool must_sort = false;
const auto add = [&] (MidiEventStream &estream, const snd_seq_event_t *ev, const MidiEvent &event) {
const auto add = [&] (MidiEventOutput &estream, const snd_seq_event_t *ev, const MidiEvent &event) {
const double t = ev->time.time.tv_sec + 1e-9 * ev->time.time.tv_nsec;
const double diff = t - now;
int64_t frames = diff * samplerate;
Expand Down
2 changes: 1 addition & 1 deletion ase/driver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ class NullMidiDriver : public MidiDriver {
return false;
}
uint
fetch_events (MidiEventStream&, double) override
fetch_events (MidiEventOutput&, double) override
{
return 0; // FIXME: needed?
}
Expand Down
2 changes: 1 addition & 1 deletion ase/driver.hh
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public:
typedef std::shared_ptr<MidiDriver> MidiDriverP;
static MidiDriverP open (const String &devid, IODir iodir, Ase::Error *ep);
virtual bool has_events () = 0;
virtual uint fetch_events (MidiEventStream &estream, double samplerate) = 0;
virtual uint fetch_events (MidiEventOutput &estream, double samplerate) = 0;
static EntryVec list_drivers ();
static String register_driver (const String &driverid,
const std::function<MidiDriverP (const String&)> &create,
Expand Down
101 changes: 66 additions & 35 deletions ase/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ class AudioEngineThread : public AudioEngine {
static constexpr uint fixed_n_channels = 2;
PcmDriverP null_pcm_driver_, pcm_driver_;
constexpr static size_t MAX_BUFFER_SIZE = AUDIO_BLOCK_MAX_RENDER_SIZE;
size_t buffer_size_ = MAX_BUFFER_SIZE; // mono buffer size
std::atomic<uint64_t> buffer_size_ = MAX_BUFFER_SIZE; // mono buffer size
float chbuffer_data_[MAX_BUFFER_SIZE * fixed_n_channels] = { 0, };
uint64 write_stamp_ = 0, render_stamp_ = MAX_BUFFER_SIZE;
uint64 write_stamp_ = 0;
std::vector<AudioProcessor*> schedule_;
EngineMidiInputP midi_proc_;
bool schedule_invalid_ = true;
Expand All @@ -81,7 +81,6 @@ class AudioEngineThread : public AudioEngine {
void schedule_clear ();
void schedule_add (AudioProcessor &aproc, uint level);
void schedule_queue_update ();
uint64 frame_counter () const { return render_stamp_; }
void schedule_render (uint64 frames);
void enable_output (AudioProcessor &aproc, bool onoff);
void wakeup_thread_mt ();
Expand All @@ -102,6 +101,7 @@ class AudioEngineThread : public AudioEngine {
void start_threads_ml ();
void stop_threads_ml ();
void create_processors_ml ();
String engine_stats_string (uint64_t stats) const;
};

static std::thread::id audio_engine_thread_id = {};
Expand Down Expand Up @@ -363,7 +363,7 @@ AudioEngineThread::driver_dispatcher (const LoopState &state)
if (schedule_invalid_)
{
schedule_clear();
for (AudioProcessorP &proc : oprocs_)
for (AudioProcessorP &proc : oprocs_)
proc->schedule_processor();
schedule_invalid_ = false;
}
Expand Down Expand Up @@ -515,6 +515,22 @@ AudioEngineThread::set_project (ProjectImplP project)
// dtor of old runs here
}

String
AudioEngineThread::engine_stats_string (uint64_t stats) const
{
String s;
for (size_t i = 0; i < oprocs_.size(); i++) {
AudioProcessorInfo pinfo;
pinfo.label = "INTERNAL";
AudioProcessor::registry_foreach ([&] (const String &aseid, AudioProcessor::StaticInfo static_info) {
if (aseid == oprocs_[i]->aseid_)
static_info (pinfo); // TODO: this is a bit awkward to fetch AudioProcessorInfo for an AudioProcessor
});
s += string_format ("%s: %s (MUST_SCHEDULE)\n", pinfo.label, oprocs_[i]->debug_name());
}
return s;
}

ProjectImplP
AudioEngineThread::get_project ()
{
Expand All @@ -532,6 +548,7 @@ AudioEngineThread::AudioEngineThread (const VoidF &owner_wakeup, uint sample_rat
AudioEngine (*this, *new (transport_block.block_start) AudioTransport (speakerarrangement, sample_rate)),
owner_wakeup_ (owner_wakeup), transport_block_ (transport_block)
{
render_stamp_ = MAX_BUFFER_SIZE; // enforce non-0 start offset for all modules
oprocs_.reserve (16);
assert_return (transport_.samplerate == 48000);
}
Expand All @@ -556,11 +573,20 @@ AudioEngine::~AudioEngine()
fatal_error ("AudioEngine must not be destroyed");
}

String
AudioEngine::engine_stats (uint64_t stats) const
{
String strstats;
const AudioEngineThread &engine_thread = static_cast<const AudioEngineThread&> (*this);
const_cast<AudioEngine*> (this)->synchronized_jobs += [&] () { strstats = engine_thread.engine_stats_string (stats); };
return strstats;
}

uint64
AudioEngine::frame_counter () const
AudioEngine::block_size() const
{
const AudioEngineThread &impl = static_cast<const AudioEngineThread&> (*this);
return impl.frame_counter();
return impl.buffer_size_;
}

void
Expand Down Expand Up @@ -764,23 +790,23 @@ class EngineMidiInput : public AudioProcessor {
void
reset (uint64 target_stamp) override
{
MidiEventStream &estream = get_event_output();
MidiEventOutput &estream = midi_event_output();
estream.clear();
estream.reserve (256);
}
void
render (uint n_frames) override
{
MidiEventStream &estream = get_event_output();
MidiEventOutput &estream = midi_event_output();
estream.clear();
for (size_t i = 0; i < midi_drivers_.size(); i++)
if (midi_drivers_[i])
midi_drivers_[i]->fetch_events (estream, sample_rate());
}
public:
MidiDriverS midi_drivers_;
EngineMidiInput (AudioEngine &engine) :
AudioProcessor (engine)
EngineMidiInput (const ProcessorSetup &psetup) :
AudioProcessor (psetup)
{}
};

Expand Down Expand Up @@ -880,38 +906,43 @@ midi_driver_pref_list_choices (const CString &ident)
}

static Preference pcm_driver_pref =
Preference ("driver.pcm.devid",
{ _("PCM Driver"), "", "auto", "ms", { pcm_driver_pref_list_choices },
STANDARD, "", _("Driver and device to be used for PCM input and output"), },
[] (const CString&,const Value&) { apply_driver_preferences(); });
Preference ({
"driver.pcm.devid", _("PCM Driver"), "", "auto", "ms",
{ pcm_driver_pref_list_choices }, STANDARD, "",
_("Driver and device to be used for PCM input and output"), },
[] (const CString&,const Value&) { apply_driver_preferences(); });

static Preference synth_latency_pref =
Preference ("driver.pcm.synth_latency",
{ _("Synth Latency"), "", 15, "ms", MinMaxStep { 0, 3000, 5 }, STANDARD + String ("step=5"),
"", _("Processing duration between input and output of a single sample, smaller values increase CPU load") },
[] (const CString&,const Value&) { apply_driver_preferences(); });
Preference ({
"driver.pcm.synth_latency", _("Synth Latency"), "", 15, "ms",
MinMaxStep { 0, 3000, 5 }, STANDARD + String ("step=5"), "",
_("Processing duration between input and output of a single sample, smaller values increase CPU load") },
[] (const CString&,const Value&) { apply_driver_preferences(); });

static Preference midi1_driver_pref =
Preference ("driver.midi1.devid",
{ _("MIDI Controller (1)"), "", "auto", "ms", { midi_driver_pref_list_choices },
STANDARD, "", _("MIDI controller device to be used for MIDI input"), },
[] (const CString&,const Value&) { apply_driver_preferences(); });
Preference ({
"driver.midi1.devid", _("MIDI Controller (1)"), "", "auto", "ms",
{ midi_driver_pref_list_choices }, STANDARD, "",
_("MIDI controller device to be used for MIDI input"), },
[] (const CString&,const Value&) { apply_driver_preferences(); });
static Preference midi2_driver_pref =
Preference ("driver.midi2.devid",
{ _("MIDI Controller (2)"), "", "auto", "ms", { midi_driver_pref_list_choices },
STANDARD, "", _("MIDI controller device to be used for MIDI input"), },
[] (const CString&,const Value&) { apply_driver_preferences(); });
Preference ({
"driver.midi2.devid", _("MIDI Controller (2)"), "", "auto", "ms",
{ midi_driver_pref_list_choices }, STANDARD, "",
_("MIDI controller device to be used for MIDI input"), },
[] (const CString&,const Value&) { apply_driver_preferences(); });
static Preference midi3_driver_pref =
Preference ("driver.midi3.devid",
{ _("MIDI Controller (3)"), "", "auto", "ms", { midi_driver_pref_list_choices },
STANDARD, "", _("MIDI controller device to be used for MIDI input"), },
[] (const CString&,const Value&) { apply_driver_preferences(); });
Preference ({
"driver.midi3.devid", _("MIDI Controller (3)"), "", "auto", "ms",
{ midi_driver_pref_list_choices }, STANDARD, "",
_("MIDI controller device to be used for MIDI input"), },
[] (const CString&,const Value&) { apply_driver_preferences(); });
static Preference midi4_driver_pref =
Preference ("driver.midi4.devid",
{ _("MIDI Controller (4)"), "", "auto", "ms", { midi_driver_pref_list_choices },
STANDARD, "", _("MIDI controller device to be used for MIDI input"), },
[] (const CString&,const Value&) { apply_driver_preferences(); });

Preference ({
"driver.midi4.devid", _("MIDI Controller (4)"), "", "auto", "ms",
{ midi_driver_pref_list_choices }, STANDARD, "",
_("MIDI controller device to be used for MIDI input"), },
[] (const CString&,const Value&) { apply_driver_preferences(); });

static void
apply_driver_preferences ()
Expand Down
Loading

0 comments on commit 8f23a2d

Please sign in to comment.