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, };
 
   62  EngineMidiInputP             midi_proc_;
 
   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);
 
  825  EngineMidiInputP midi_proc = midi_proc_;
 
  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.