18#define EDEBUG(...) Ase::debug ("engine", __VA_ARGS__)
22constexpr const uint FIXED_N_CHANNELS = 2;
23constexpr const uint FIXED_SAMPLE_RATE = 48000;
24constexpr const uint FIXED_N_MIDI_DRIVERS = 4;
28using StartQueue = AsyncBlockingQueue<char>;
29ASE_CLASS_DECLS (EngineMidiInput);
30static void apply_driver_preferences ();
49 MidiDriverS midi_drivers;
55 static constexpr uint fixed_n_channels = 2;
57 constexpr static size_t MAX_BUFFER_SIZE = AUDIO_BLOCK_MAX_RENDER_SIZE;
59 float chbuffer_data_[MAX_BUFFER_SIZE * fixed_n_channels] = { 0, };
63 bool schedule_invalid_ =
true;
64 bool output_needsrunning_ =
false;
66 const VoidF owner_wakeup_;
69 AudioProcessorS oprocs_;
83 void schedule_clear ();
85 void schedule_queue_update ();
86 void schedule_render (
uint64 frames);
88 void wakeup_thread_mt ();
89 void capture_start (
const String &filename,
bool needsrunning);
93 AudioProcessorP get_event_source ();
95 bool pcm_check_write (
bool write_buffer,
int64 *timeout_usecs_p =
nullptr);
96 bool driver_dispatcher (
const LoopState &state);
99 void queue_user_note (
const String &channel, UserNote::Flags flags,
const String &text);
102 void update_driver_set (
DriverSet &dset);
103 void start_threads_ml ();
104 void stop_threads_ml ();
105 void create_processors_ml ();
106 String engine_stats_string (uint64_t stats)
const;
110const ThreadId &AudioEngine::thread_id = audio_engine_thread_id;
113atomic_next_ptrref (AudioEngineThread::UserNoteJob *j)
118template<
int ADDING>
static void
119interleaved_stereo (
const size_t n_frames,
float *buffer, AudioProcessor &proc,
OBusId obus)
121 if (proc.n_ochannels (obus) >= 2)
123 const float *src0 = proc.ofloats (obus, 0);
124 const float *src1 = proc.ofloats (obus, 1);
125 float *d = buffer, *
const b = d + n_frames;
139 else if (proc.n_ochannels (obus) >= 1)
141 const float *src = proc.ofloats (obus, 0);
142 float *d = buffer, *
const b = d + n_frames;
159AudioEngineThread::schedule_queue_update()
161 schedule_invalid_ =
true;
165AudioEngineThread::schedule_clear()
167 while (schedule_.size() != 0)
169 AudioProcessor *cur = schedule_.back();
170 schedule_.pop_back();
173 AudioProcessor *
const proc = cur;
174 cur = proc->sched_next_;
175 proc->flags_ &= ~AudioProcessor::SCHEDULED;
176 proc->sched_next_ =
nullptr;
179 schedule_invalid_ =
true;
183AudioEngineThread::schedule_add (AudioProcessor &aproc,
uint level)
185 return_unless (0 == (aproc.flags_ & AudioProcessor::SCHEDULED));
187 if (schedule_.size() <= level)
188 schedule_.resize (level + 1);
189 aproc.sched_next_ = schedule_[level];
190 schedule_[level] = &aproc;
191 aproc.flags_ |= AudioProcessor::SCHEDULED;
192 if (aproc.render_stamp_ != render_stamp_)
193 aproc.reset_state (render_stamp_);
197AudioEngineThread::schedule_render (
uint64 frames)
201 const uint64 target_stamp = render_stamp_ + frames;
202 for (
size_t l = 0; l < schedule_.size(); l++)
204 AudioProcessor *proc = schedule_[l];
207 proc->render_block (target_stamp);
208 proc = proc->sched_next_;
212 constexpr auto MAIN_OBUS =
OBusId (1);
214 for (
size_t i = 0; i < oprocs_.size(); i++)
215 if (oprocs_[i]->n_obuses())
218 interleaved_stereo<0> (buffer_size_ * fixed_n_channels, chbuffer_data_, *oprocs_[i], MAIN_OBUS);
220 interleaved_stereo<1> (buffer_size_ * fixed_n_channels, chbuffer_data_, *oprocs_[i], MAIN_OBUS);
221 static_assert (2 == fixed_n_channels);
224 floatfill (chbuffer_data_, 0.0, buffer_size_ * fixed_n_channels);
225 render_stamp_ = target_stamp;
226 transport_.advance (frames);
230AudioEngineThread::enable_output (AudioProcessor &aproc,
bool onoff)
232 AudioProcessorP procp = shared_ptr_cast<AudioProcessor> (&aproc);
234 if (onoff && !(aproc.flags_ & AudioProcessor::ENGINE_OUTPUT))
236 oprocs_.push_back (procp);
237 aproc.flags_ |= AudioProcessor::ENGINE_OUTPUT;
238 schedule_queue_update();
240 else if (!onoff && (aproc.flags_ & AudioProcessor::ENGINE_OUTPUT))
242 const bool foundproc =
Aux::erase_first (oprocs_, [procp] (AudioProcessorP c) {
return c == procp; });
243 aproc.flags_ &= ~AudioProcessor::ENGINE_OUTPUT;
244 schedule_queue_update();
250AudioEngineThread::capture_start (
const String &filename,
bool needsrunning)
254 output_needsrunning_ = needsrunning;
257 wwriter_ = wave_writer_create_wav (sample_rate, fixed_n_channels, filename);
259 printerr (
"%s: failed to open file: %s\n", filename, strerror (errno));
263 wwriter_ = wave_writer_create_opus (sample_rate, fixed_n_channels, filename);
265 printerr (
"%s: failed to open file: %s\n", filename, strerror (errno));
269 wwriter_ = wave_writer_create_flac (sample_rate, fixed_n_channels, filename);
271 printerr (
"%s: failed to open file: %s\n", filename, strerror (errno));
273 else if (!filename.empty())
274 printerr (
"%s: unknown sample file: %s\n", filename, strerror (ENOSYS));
278AudioEngineThread::capture_stop()
288AudioEngineThread::run (StartQueue *sq)
293 pcm_driver_ = null_pcm_driver_;
294 floatfill (chbuffer_data_, 0.0, MAX_BUFFER_SIZE * fixed_n_channels);
295 buffer_size_ =
std::min (MAX_BUFFER_SIZE,
size_t (pcm_driver_->pcm_block_length()));
296 write_stamp_ = render_stamp_ - buffer_size_;
297 this_thread_set_name (
"AudioEngine-0");
300 event_loop_->exec_dispatcher (
std::bind (&AudioEngineThread::driver_dispatcher,
this, std::placeholders::_1));
308AudioEngineThread::process_jobs (AtomicIntrusiveStack<EngineJobImpl> &joblist)
310 EngineJobImpl *
const jobs = joblist.pop_reversed(), *last =
nullptr;
311 for (EngineJobImpl *job = jobs; job; last = job, job = job->next)
315 if (trash_jobs_.push_chain (jobs, last))
318 return last !=
nullptr;
322AudioEngineThread::pcm_check_write (
bool write_buffer,
int64 *timeout_usecs_p)
325 const bool can_write = pcm_driver_->pcm_check_io (&timeout_usecs) || timeout_usecs == 0;
327 *timeout_usecs_p = timeout_usecs;
330 if (!can_write || write_stamp_ >= render_stamp_)
332 pcm_driver_->pcm_write (buffer_size_ * fixed_n_channels, chbuffer_data_);
333 if (wwriter_ && fixed_n_channels == 2 && write_stamp_ < autostop_ &&
334 (!output_needsrunning_ || transport_.running()))
335 wwriter_->write (chbuffer_data_, buffer_size_);
336 write_stamp_ += buffer_size_;
337 if (write_stamp_ >= autostop_)
338 main_loop_autostop_mt();
344AudioEngineThread::driver_dispatcher (
const LoopState &state)
346 int64 *timeout_usecs =
nullptr;
349 case LoopState::PREPARE:
350 timeout_usecs =
const_cast<int64*
> (&state.timeout_usecs);
352 case LoopState::CHECK:
353 if (atquit_triggered())
355 if (!const_jobs_.empty() || !async_jobs_.empty())
357 if (render_stamp_ <= write_stamp_)
359 return pcm_check_write (
false, timeout_usecs);
360 case LoopState::DISPATCH:
361 pcm_check_write (
true);
362 if (render_stamp_ <= write_stamp_)
364 process_jobs (async_jobs_);
365 if (schedule_invalid_)
368 for (AudioProcessorP &proc : oprocs_)
369 proc->schedule_processor();
370 schedule_invalid_ =
false;
372 if (render_stamp_ <= write_stamp_)
373 schedule_render (buffer_size_);
374 pcm_check_write (
true);
376 if (!const_jobs_.empty()) {
377 process_jobs (async_jobs_);
378 process_jobs (const_jobs_);
389AudioEngineThread::queue_user_note (
const String &channel, UserNote::Flags flags,
const String &text)
391 UserNoteJob *uj =
new UserNoteJob {
nullptr, { 0, flags, channel, text } };
392 if (user_notes_.push (uj))
397AudioEngineThread::ipc_pending ()
399 const bool have_jobs = !trash_jobs_.empty() || !user_notes_.empty();
400 return have_jobs || AudioProcessor::enotify_pending();
404AudioEngineThread::ipc_dispatch ()
406 UserNoteJob *uj = user_notes_.pop_reversed();
409 ASE_SERVER.user_note (uj->note.text, uj->note.channel, uj->note.flags);
410 UserNoteJob *
const old = uj;
414 if (AudioProcessor::enotify_pending())
415 AudioProcessor::enotify_dispatch();
416 EngineJobImpl *job = trash_jobs_.pop_all();
419 EngineJobImpl *old = job;
426AudioEngineThread::wakeup_thread_mt()
429 event_loop_->wakeup();
433AudioEngineThread::start_threads_ml()
438 schedule_.reserve (8192);
439 create_processors_ml();
440 update_drivers (
"null", 0, {});
441 null_pcm_driver_ = driver_set_ml.null_pcm_driver;
442 schedule_queue_update();
443 StartQueue start_queue;
444 thread_ =
new std::thread (&AudioEngineThread::run,
this, &start_queue);
445 const char reply = start_queue.pop();
447 apply_driver_preferences();
451AudioEngineThread::stop_threads_ml()
455 event_loop_->quit (0);
457 audio_engine_thread_id = {};
458 auto oldthread = thread_;
464AudioEngineThread::add_job_mt (EngineJobImpl *job,
const AudioEngine::JobQueue *jobqueue)
467 AudioEngineThread &engine = *
dynamic_cast<AudioEngineThread*
> (
this);
478 const bool was_empty = engine.async_jobs_.push (job);
485 VoidFunc jobfunc = job->func;
493 need_wakeup = engine.const_jobs_.push (job);
494 else if (jobqueue == &synchronized_jobs)
495 need_wakeup = engine.async_jobs_.push (job);
504AudioEngineThread::set_project (ProjectImplP project)
512 project_->_deactivate();
513 const ProjectImplP old = project_;
516 project_->_activate();
521AudioEngineThread::engine_stats_string (uint64_t stats)
const
524 for (
size_t i = 0; i < oprocs_.size(); i++) {
525 AudioProcessorInfo pinfo;
526 pinfo.label =
"INTERNAL";
528 if (aseid == oprocs_[i]->aseid_)
531 s +=
string_format (
"%s: %s (MUST_SCHEDULE)\n", pinfo.label, oprocs_[i]->debug_name());
537AudioEngineThread::get_project ()
542AudioEngineThread::~AudioEngineThread ()
544 FastMemory::Block transport_block = transport_block_;
545 main_jobs += [transport_block] () { ServerImpl::instancep()->telemem_release (transport_block); };
548AudioEngineThread::AudioEngineThread (
const VoidF &owner_wakeup,
uint sample_rate,
SpeakerArrangement speakerarrangement,
549 const FastMemory::Block &transport_block) :
550 AudioEngine (*this, *new (transport_block.block_start) AudioTransport (speakerarrangement, sample_rate)),
551 owner_wakeup_ (owner_wakeup), transport_block_ (transport_block)
553 render_stamp_ = MAX_BUFFER_SIZE;
554 oprocs_.reserve (16);
562 ASE_ASSERT_WARN (speaker_arrangement_count_channels (speakerarrangement) == FIXED_N_CHANNELS);
563 FastMemory::Block transport_block = ServerImpl::instancep()->telemem_allocate (
sizeof (AudioTransport));
564 return *
new AudioEngineThread (owner_wakeup, sample_rate, speakerarrangement, transport_block);
568AudioEngine::AudioEngine (AudioEngineThread &audio_engine_thread, AudioTransport &transport) :
569 transport_ (transport),
async_jobs (audio_engine_thread),
const_jobs (audio_engine_thread), synchronized_jobs (audio_engine_thread)
572AudioEngine::~AudioEngine()
575 fatal_error (
"AudioEngine must not be destroyed");
579AudioEngine::engine_stats (uint64_t stats)
const
582 const AudioEngineThread &engine_thread =
static_cast<const AudioEngineThread&
> (*this);
583 const_cast<AudioEngine*
> (
this)->synchronized_jobs += [&] () { strstats = engine_thread.engine_stats_string (stats); };
588AudioEngine::block_size()
const
590 const AudioEngineThread &impl =
static_cast<const AudioEngineThread&
> (*this);
591 return impl.buffer_size_;
595AudioEngine::set_autostop (uint64_t nsamples)
597 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
598 impl.autostop_ = nsamples;
602AudioEngine::schedule_queue_update()
604 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
605 impl.schedule_queue_update();
609AudioEngine::schedule_add (AudioProcessor &aproc,
uint level)
611 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
612 impl.schedule_add (aproc, level);
616AudioEngine::enable_output (AudioProcessor &aproc,
bool onoff)
618 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
619 return impl.enable_output (aproc, onoff);
623AudioEngine::start_threads()
625 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
626 return impl.start_threads_ml();
630AudioEngine::stop_threads()
632 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
633 return impl.stop_threads_ml();
637AudioEngine::queue_capture_start (CallbackS &callbacks,
const String &filename,
bool needsrunning)
639 AudioEngineThread *impl =
static_cast<AudioEngineThread*
> (
this);
641 callbacks.
push_back ([impl,file,needsrunning] () {
642 impl->capture_start (file, needsrunning);
647AudioEngine::queue_capture_stop (CallbackS &callbacks)
649 AudioEngineThread *impl =
static_cast<AudioEngineThread*
> (
this);
650 callbacks.push_back ([impl] () {
651 impl->capture_stop();
656AudioEngine::wakeup_thread_mt ()
658 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
659 return impl.wakeup_thread_mt();
663AudioEngine::ipc_pending ()
665 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
666 return impl.ipc_pending();
670AudioEngine::ipc_dispatch ()
672 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
673 return impl.ipc_dispatch();
677AudioEngine::get_event_source ()
679 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
680 return impl.get_event_source();
684AudioEngine::set_project (ProjectImplP project)
686 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
687 return impl.set_project (project);
691AudioEngine::get_project ()
693 AudioEngineThread &impl =
static_cast<AudioEngineThread&
> (*this);
694 return impl.get_project();
697AudioEngine::JobQueue::JobQueue (AudioEngine &aet) :
698 queue_tag_ (ptrdiff_t (this) - ptrdiff_t (&aet))
704AudioEngine::JobQueue::operator+= (
const std::function<
void()> &job)
706 AudioEngine *audio_engine =
reinterpret_cast<AudioEngine*
> (ptrdiff_t (
this) - queue_tag_);
707 AudioEngineThread &audio_engine_thread =
static_cast<AudioEngineThread&
> (*audio_engine);
708 return audio_engine_thread.add_job_mt (
new EngineJobImpl (job),
this);
712AudioEngine::update_drivers (
const String &pcm_name,
uint latency_ms,
const StringS &midi_prefs)
714 AudioEngineThread &engine_thread =
static_cast<AudioEngineThread&
> (*this);
715 DriverSet &dset = engine_thread.driver_set_ml;
716 const char *
const null_driver =
"null";
719 const PcmDriverConfig pcm_config { .n_channels = engine_thread.fixed_n_channels, .mix_freq = FIXED_SAMPLE_RATE,
720 .block_length = AUDIO_BLOCK_MAX_RENDER_SIZE, .latency_ms = latency_ms };
722 if (!dset.null_pcm_driver) {
725 dset.null_pcm_driver = PcmDriver::open (null_driver, Driver::WRITEONLY, Driver::WRITEONLY, pcm_config, &er);
726 if (!dset.null_pcm_driver || er != 0)
727 fatal_error (
"failed to open internal PCM driver ('%s'): %s", null_driver,
ase_error_blurb (er));
730 if (pcm_name != dset.pcm_name) {
732 dset.pcm_name = pcm_name;
734 dset.pcm_driver = dset.pcm_name == null_driver ? dset.null_pcm_driver :
735 PcmDriver::open (dset.pcm_name, Driver::WRITEONLY, Driver::WRITEONLY, pcm_config, &er);
736 if (!dset.pcm_driver || er != 0) {
737 dset.pcm_driver = dset.null_pcm_driver;
738 log (
"Audio Driver: Failed to open audio device: %s: %s", dset.pcm_name,
ase_error_blurb (er));
739 const String errmsg =
string_format (
"# Audio I/O Error\n" "Failed to open audio device:\n" "%s:\n" "%s",
741 engine_thread.queue_user_note (
"driver.pcm", UserNote::CLEAR, errmsg);
747 midis.
resize (FIXED_N_MIDI_DRIVERS);
748 for (
size_t i = 0; i < midis.size(); i++)
749 if (midis[i].
empty())
750 midis[i] = null_driver;
752 for (
size_t j = 0; j < i; j++)
753 if (midis[i] != null_driver && midis[i] == midis[j]) {
754 midis[i] = null_driver;
758 dset.midi_names.resize (midis.size());
759 dset.midi_drivers.resize (dset.midi_names.size());
760 for (
size_t i = 0; i < dset.midi_drivers.size(); i++) {
761 if (midis[i] == dset.midi_names[i])
764 dset.midi_names[i] = midis[i];
766 dset.midi_drivers[i] = dset.midi_names[i] == null_driver ? nullptr :
767 MidiDriver::open (dset.midi_names[i], Driver::READONLY, &er);
769 dset.midi_drivers[i] =
nullptr;
770 const String errmsg =
string_format (
"# MIDI I/O Error\n" "Failed to open MIDI device #%u:\n" "%s:\n" "%s",
772 engine_thread.queue_user_note (
"driver.midi", UserNote::CLEAR, errmsg);
778 Mutable<DriverSet> mdset = dset;
779 synchronized_jobs += [mdset,&engine_thread] () { engine_thread.update_driver_set (mdset.value); };
791 prepare_event_output();
794 reset (
uint64 target_stamp)
override
798 estream.reserve (256);
801 render (
uint n_frames)
override
805 for (
size_t i = 0; i < midi_drivers_.size(); i++)
806 if (midi_drivers_[i])
807 midi_drivers_[i]->fetch_events (estream, sample_rate());
810 MidiDriverS midi_drivers_;
817AudioEngineThread::create_processors_ml ()
821 AudioProcessorP aprocp = AudioProcessor::create_processor<EngineMidiInput> (*
this);
827 midi_proc->enable_engine_output (
true);
832AudioEngineThread::get_event_source ()
838AudioEngineThread::update_driver_set (DriverSet &dset)
843 if (pcm_driver_ != dset.pcm_driver) {
844 pcm_driver_.
swap (dset.pcm_driver);
845 floatfill (chbuffer_data_, 0.0, MAX_BUFFER_SIZE * fixed_n_channels);
846 buffer_size_ =
std::min (MAX_BUFFER_SIZE,
size_t (pcm_driver_->pcm_block_length()));
847 write_stamp_ = render_stamp_ - buffer_size_;
848 EDEBUG (
"AudioEngineThread::%s: update PCM to \"%s\": channels=%d pcmblock=%d enginebuffer=%d ws=%u rs=%u bs=%u\n", __func__,
849 dset.pcm_name, fixed_n_channels, pcm_driver_->pcm_block_length(), buffer_size_, write_stamp_, render_stamp_, buffer_size_);
852 if (midi_proc_->midi_drivers_ != dset.midi_drivers) {
853 midi_proc_->midi_drivers_.
swap (dset.midi_drivers);
854 EDEBUG (
"AudioEngineThread::%s: swapping %u MIDI drivers: \"%s\"\n", __func__, midi_proc_->midi_drivers_.size(),
string_join (
"\" \"", dset.midi_names));
860choice_from_driver_entry (
const DriverEntry &e,
const String &icon_keywords)
863 if (!e.device_info.empty() && !e.capabilities.empty())
864 blurb = e.capabilities +
"\n" + e.device_info;
865 else if (!e.capabilities.empty())
866 blurb = e.capabilities;
868 blurb = e.device_info;
869 Choice c (e.devid, e.device_name, blurb);
871 c.warning = e.notice;
878 c.icon = MakeIcon::KwIcon (icon_keywords +
"," + e.hints);
883pcm_driver_pref_list_choices (
const CString &ident)
885 static ChoiceS choices;
886 static uint64 cache_age = 0;
889 for (
const DriverEntry &e : PcmDriver::list_drivers())
890 choices.push_back (choice_from_driver_entry (e,
"pcm"));
897midi_driver_pref_list_choices (
const CString &ident)
899 static ChoiceS choices;
900 static uint64 cache_age = 0;
903 for (
const DriverEntry &e : MidiDriver::list_drivers())
905 choices.push_back (choice_from_driver_entry (e,
"midi"));
911static Preference pcm_driver_pref =
913 "driver.pcm.devid",
_(
"PCM Driver"),
"",
"auto",
"ms",
914 { pcm_driver_pref_list_choices },
STANDARD, {
915 String (
"descr=") +
_(
"Driver and device to be used for PCM input and output"), } },
916 [] (
const CString&,
const Value&) { apply_driver_preferences(); });
918static Preference synth_latency_pref =
920 "driver.pcm.synth_latency",
_(
"Synth Latency"),
"", 15,
"ms",
922 String (
"descr=") +
_(
"Processing duration between input and output of a single sample, smaller values increase CPU load"), } },
923 [] (
const CString&,
const Value&) { apply_driver_preferences(); });
925static Preference midi1_driver_pref =
927 "driver.midi1.devid",
_(
"MIDI Controller (1)"),
"",
"auto",
"ms",
928 { midi_driver_pref_list_choices },
STANDARD, {
929 String (
"descr=") +
_(
"MIDI controller device to be used for MIDI input"), } },
930 [] (
const CString&,
const Value&) { apply_driver_preferences(); });
931static Preference midi2_driver_pref =
933 "driver.midi2.devid",
_(
"MIDI Controller (2)"),
"",
"auto",
"ms",
934 { midi_driver_pref_list_choices },
STANDARD, {
935 String (
"descr=") +
_(
"MIDI controller device to be used for MIDI input"), } },
936 [] (
const CString&,
const Value&) { apply_driver_preferences(); });
937static Preference midi3_driver_pref =
939 "driver.midi3.devid",
_(
"MIDI Controller (3)"),
"",
"auto",
"ms",
940 { midi_driver_pref_list_choices },
STANDARD, {
941 String (
"descr=") +
_(
"MIDI controller device to be used for MIDI input"), } },
942 [] (
const CString&,
const Value&) { apply_driver_preferences(); });
943static Preference midi4_driver_pref =
945 "driver.midi4.devid",
_(
"MIDI Controller (4)"),
"",
"auto",
"ms",
946 { midi_driver_pref_list_choices },
STANDARD, {
947 String (
"descr=") +
_(
"MIDI controller device to be used for MIDI input"), } },
948 [] (
const CString&,
const Value&) { apply_driver_preferences(); });
951apply_driver_preferences ()
953 static uint engine_driver_set_timerid = 0;
954 main_loop->exec_once (97, &engine_driver_set_timerid,
956 String pcm_driver = pcm_driver_pref.gets();
957 if (!App.pcm_override.
empty())
958 pcm_driver = App.pcm_override;
959 StringS midis = { midi1_driver_pref.gets(), midi2_driver_pref.gets(), midi3_driver_pref.gets(), midi4_driver_pref.gets(), };
960 if (!App.midi_override.
empty())
961 midis = { App.midi_override,
"null",
"null",
"null", };
962 App.engine->update_drivers (pcm_driver, synth_latency_pref.getn(), midis);
This is a thread-safe asyncronous queue which blocks in pop() until data is provided through push().
JobQueue async_jobs
Executed asynchronously, may modify AudioProcessor objects.
JobQueue const_jobs
Blocks during execution, must treat AudioProcessor objects read-only.
Audio signal AudioProcessor base class, implemented by all effects and instruments.
static void registry_foreach(const std::function< void(const String &aseid, StaticInfo)> &fun)
Iterate over the known AudioProcessor types.
static MainLoopP create()
Create a MainLoop shared pointer handle.
A stream of writable MidiEvent structures.
#define ASE_ASSERT_WARN(expr)
Issue an assertion warning if expr evaluates to false.
#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 assert_warn(expr)
Issue an assertion warning if expr evaluates to false.
#define _(...)
Retrieve the translation of a C or C++ string.
#define if_constexpr
Indentation helper for editors that cannot (yet) decipher if constexpr
#define assert_unreached()
Explicitely mark unreachable code locations.
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.
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
void floatfill(float *dst, float f, size_t n)
Fill n values of dst with f.
String string_join(const String &junctor, const StringS &strvec)
OBusId
ID type for AudioProcessor output buses, buses are numbered with increasing index.
String string_tolower(const String &str)
Convert all string characters into Unicode lower case characters.
std::tuple< double, double, double > MinMaxStep
Min, max, stepping for double ranges.
std::vector< String > StringS
Convenience alias for a std::vector<std::string>.
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
JobQueue main_jobs(call_main_loop)
Execute a job callback in the Ase main loop.
Error
Enum representing Error states.
const char * ase_error_blurb(Error error)
Describe Error condition.
String string_replace(const String &input, const String &marker, const String &replacement, size_t maxn)
Replace substring marker in input with replacement, at most maxn times.
bool sched_fast_priority(int tid)
Try to acquire low latency scheduling priority, returns true if nice level is < 0.
std::string String
Convenience alias for std::string.
constexpr const char STANDARD[]
STORAGE GUI READABLE WRITABLE.
uint32_t uint
Provide 'uint' as convenience type.
bool string_endswith(const String &string, const String &fragment)
Returns whether string ends with fragment.
uint64 timestamp_realtime()
Return the current time as uint64 in µseconds.
bool string_startswith(const String &string, const String &fragment)
Returns whether string starts with fragment.
const uint samplerate
Sample rate (mixing frequency) in Hz used for rendering.
Reference for an allocated memory block.
Contents of user interface notifications.