Merge branch 'ardour'
This commit is contained in:
commit
18115f5383
|
@ -477,7 +477,7 @@ ExportDialog::show_progress ()
|
||||||
|
|
||||||
if (!status->aborted()) {
|
if (!status->aborted()) {
|
||||||
hide();
|
hide();
|
||||||
if (!ARDOUR::Profile->get_mixbus()) {
|
if (!ARDOUR::Profile->get_mixbus () && !ARDOUR::Profile->get_livetrax ()) {
|
||||||
NagScreen* ns = NagScreen::maybe_nag (_("export"));
|
NagScreen* ns = NagScreen::maybe_nag (_("export"));
|
||||||
if (ns) {
|
if (ns) {
|
||||||
ns->nag ();
|
ns->nag ();
|
||||||
|
|
|
@ -3,4 +3,3 @@
|
||||||
#else
|
#else
|
||||||
#include "ardour_rc_option_editor.cc"
|
#include "ardour_rc_option_editor.cc"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -345,7 +345,7 @@ SimpleExportDialog::start_export ()
|
||||||
if (_post_export_combo.get_active_row_number () == 0) {
|
if (_post_export_combo.get_active_row_number () == 0) {
|
||||||
PBD::open_folder (folder ());
|
PBD::open_folder (folder ());
|
||||||
}
|
}
|
||||||
if (!ARDOUR::Profile->get_mixbus ()) {
|
if (!ARDOUR::Profile->get_mixbus () && !ARDOUR::Profile->get_livetrax ()) {
|
||||||
NagScreen* ns = NagScreen::maybe_nag (_("Export"));
|
NagScreen* ns = NagScreen::maybe_nag (_("Export"));
|
||||||
if (ns) {
|
if (ns) {
|
||||||
ns->nag ();
|
ns->nag ();
|
||||||
|
|
|
@ -58,6 +58,7 @@ namespace PBD {
|
||||||
LIBARDOUR_API extern DebugBits FaderPort;
|
LIBARDOUR_API extern DebugBits FaderPort;
|
||||||
LIBARDOUR_API extern DebugBits GenericMidi;
|
LIBARDOUR_API extern DebugBits GenericMidi;
|
||||||
LIBARDOUR_API extern DebugBits Graph;
|
LIBARDOUR_API extern DebugBits Graph;
|
||||||
|
LIBARDOUR_API extern DebugBits IOTaskList;
|
||||||
LIBARDOUR_API extern DebugBits LTC;
|
LIBARDOUR_API extern DebugBits LTC;
|
||||||
LIBARDOUR_API extern DebugBits LV2;
|
LIBARDOUR_API extern DebugBits LV2;
|
||||||
LIBARDOUR_API extern DebugBits LV2Automate;
|
LIBARDOUR_API extern DebugBits LV2Automate;
|
||||||
|
|
|
@ -38,72 +38,72 @@ class MidiPlaylist;
|
||||||
|
|
||||||
template <typename T> class MidiRingBuffer;
|
template <typename T> class MidiRingBuffer;
|
||||||
|
|
||||||
class LIBARDOUR_API DiskReader : public DiskIOProcessor
|
class /*LIBARDOUR_API*/ DiskReader : public DiskIOProcessor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DiskReader (Session&, Track&, std::string const& name, Temporal::TimeDomainProvider const &, DiskIOProcessor::Flag f = DiskIOProcessor::Flag (0));
|
LIBARDOUR_API DiskReader (Session&, Track&, std::string const& name, Temporal::TimeDomainProvider const &, DiskIOProcessor::Flag f = DiskIOProcessor::Flag (0));
|
||||||
~DiskReader ();
|
LIBARDOUR_API ~DiskReader ();
|
||||||
|
|
||||||
bool set_name (std::string const& str);
|
LIBARDOUR_API bool set_name (std::string const& str);
|
||||||
|
|
||||||
std::string display_name () const;
|
LIBARDOUR_API std::string display_name () const;
|
||||||
|
|
||||||
static samplecnt_t chunk_samples ()
|
LIBARDOUR_API static samplecnt_t chunk_samples ()
|
||||||
{
|
{
|
||||||
return _chunk_samples;
|
return _chunk_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_chunk_samples (samplecnt_t n)
|
LIBARDOUR_API static void set_chunk_samples (samplecnt_t n)
|
||||||
{
|
{
|
||||||
_chunk_samples = n;
|
_chunk_samples = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static samplecnt_t default_chunk_samples ();
|
LIBARDOUR_API static samplecnt_t default_chunk_samples ();
|
||||||
|
|
||||||
void run (BufferSet& /*bufs*/, samplepos_t /*start_sample*/, samplepos_t /*end_sample*/, double speed, pframes_t /*nframes*/, bool /*result_required*/);
|
LIBARDOUR_API void run (BufferSet& /*bufs*/, samplepos_t /*start_sample*/, samplepos_t /*end_sample*/, double speed, pframes_t /*nframes*/, bool /*result_required*/);
|
||||||
void realtime_handle_transport_stopped ();
|
LIBARDOUR_API void realtime_handle_transport_stopped ();
|
||||||
void realtime_locate (bool);
|
LIBARDOUR_API void realtime_locate (bool);
|
||||||
bool overwrite_existing_buffers ();
|
LIBARDOUR_API bool overwrite_existing_buffers ();
|
||||||
void set_pending_overwrite (OverwriteReason);
|
LIBARDOUR_API void set_pending_overwrite (OverwriteReason);
|
||||||
void set_loop (Location*);
|
LIBARDOUR_API void set_loop (Location*);
|
||||||
|
|
||||||
int set_state (const XMLNode&, int version);
|
LIBARDOUR_API int set_state (const XMLNode&, int version);
|
||||||
|
|
||||||
PBD::Signal0<void> AlignmentStyleChanged;
|
PBD::Signal0<void> AlignmentStyleChanged;
|
||||||
|
|
||||||
float buffer_load () const;
|
LIBARDOUR_API float buffer_load () const;
|
||||||
|
|
||||||
void move_processor_automation (std::weak_ptr<Processor>, std::list<Temporal::RangeMove> const&);
|
LIBARDOUR_API void move_processor_automation (std::weak_ptr<Processor>, std::list<Temporal::RangeMove> const&);
|
||||||
|
|
||||||
/* called by the Butler in a non-realtime context as part of its normal
|
/* called by the Butler in a non-realtime context as part of its normal
|
||||||
* buffer refill loop (not due to transport-mechanism requests like
|
* buffer refill loop (not due to transport-mechanism requests like
|
||||||
* locate)
|
* locate)
|
||||||
*/
|
*/
|
||||||
int do_refill ();
|
LIBARDOUR_API int do_refill ();
|
||||||
|
|
||||||
/** For contexts outside the normal butler refill loop (allocates temporary working buffers) */
|
/** For contexts outside the normal butler refill loop (allocates temporary working buffers) */
|
||||||
int do_refill_with_alloc (bool partial_fill, bool reverse);
|
int do_refill_with_alloc (bool partial_fill, bool reverse);
|
||||||
|
|
||||||
bool pending_overwrite () const;
|
LIBARDOUR_API bool pending_overwrite () const;
|
||||||
|
|
||||||
/* Working buffers for do_refill (butler thread) */
|
/* Working buffers for do_refill (butler thread) */
|
||||||
static void allocate_working_buffers ();
|
LIBARDOUR_API static void allocate_working_buffers ();
|
||||||
static void free_working_buffers ();
|
LIBARDOUR_API static void free_working_buffers ();
|
||||||
|
|
||||||
void adjust_buffering ();
|
LIBARDOUR_API void adjust_buffering ();
|
||||||
|
|
||||||
bool can_internal_playback_seek (sampleoffset_t distance);
|
LIBARDOUR_API bool can_internal_playback_seek (sampleoffset_t distance);
|
||||||
void internal_playback_seek (sampleoffset_t distance);
|
LIBARDOUR_API void internal_playback_seek (sampleoffset_t distance);
|
||||||
int seek (samplepos_t sample, bool complete_refill = false);
|
LIBARDOUR_API int seek (samplepos_t sample, bool complete_refill = false);
|
||||||
|
|
||||||
static PBD::Signal0<void> Underrun;
|
LIBARDOUR_API static PBD::Signal0<void> Underrun;
|
||||||
|
|
||||||
void playlist_modified ();
|
LIBARDOUR_API void playlist_modified ();
|
||||||
void reset_tracker ();
|
LIBARDOUR_API void reset_tracker ();
|
||||||
|
|
||||||
bool declick_in_progress () const;
|
LIBARDOUR_API bool declick_in_progress () const;
|
||||||
|
|
||||||
void set_need_midi_catchup (bool);
|
LIBARDOUR_API void set_need_midi_catchup (bool);
|
||||||
|
|
||||||
/* inc/dec variants MUST be called as part of the process call tree, before any
|
/* inc/dec variants MUST be called as part of the process call tree, before any
|
||||||
* disk readers are invoked. We use it when the session needs the
|
* disk readers are invoked. We use it when the session needs the
|
||||||
|
@ -112,21 +112,21 @@ public:
|
||||||
* don't want any actual disk output yet because we are still not
|
* don't want any actual disk output yet because we are still not
|
||||||
* synced.
|
* synced.
|
||||||
*/
|
*/
|
||||||
static void inc_no_disk_output ();
|
LIBARDOUR_API static void inc_no_disk_output ();
|
||||||
static void dec_no_disk_output ();
|
LIBARDOUR_API static void dec_no_disk_output ();
|
||||||
static bool no_disk_output ()
|
LIBARDOUR_API static bool no_disk_output ()
|
||||||
{
|
{
|
||||||
return _no_disk_output.load ();
|
return _no_disk_output.load ();
|
||||||
}
|
}
|
||||||
static void reset_loop_declick (Location*, samplecnt_t sample_rate);
|
LIBARDOUR_API static void reset_loop_declick (Location*, samplecnt_t sample_rate);
|
||||||
static void alloc_loop_declick (samplecnt_t sample_rate);
|
LIBARDOUR_API static void alloc_loop_declick (samplecnt_t sample_rate);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Track;
|
friend class Track;
|
||||||
friend class MidiTrack;
|
friend class MidiTrack;
|
||||||
|
|
||||||
struct ReaderChannelInfo : public DiskIOProcessor::ChannelInfo {
|
struct ReaderChannelInfo : public DiskIOProcessor::ChannelInfo {
|
||||||
ReaderChannelInfo (samplecnt_t buffer_size, samplecnt_t preloop_size)
|
LIBARDOUR_API ReaderChannelInfo (samplecnt_t buffer_size, samplecnt_t preloop_size)
|
||||||
: DiskIOProcessor::ChannelInfo (buffer_size)
|
: DiskIOProcessor::ChannelInfo (buffer_size)
|
||||||
, pre_loop_buffer (0)
|
, pre_loop_buffer (0)
|
||||||
, pre_loop_buffer_size (0)
|
, pre_loop_buffer_size (0)
|
||||||
|
@ -135,27 +135,27 @@ protected:
|
||||||
resize (buffer_size);
|
resize (buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ReaderChannelInfo ()
|
LIBARDOUR_API ~ReaderChannelInfo ()
|
||||||
{
|
{
|
||||||
delete[] pre_loop_buffer;
|
delete[] pre_loop_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void resize (samplecnt_t);
|
LIBARDOUR_API void resize (samplecnt_t);
|
||||||
void resize_preloop (samplecnt_t);
|
LIBARDOUR_API void resize_preloop (samplecnt_t);
|
||||||
|
|
||||||
Sample* pre_loop_buffer;
|
Sample* pre_loop_buffer;
|
||||||
samplecnt_t pre_loop_buffer_size;
|
samplecnt_t pre_loop_buffer_size;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
};
|
};
|
||||||
|
|
||||||
XMLNode& state () const;
|
LIBARDOUR_API XMLNode& state () const;
|
||||||
|
|
||||||
void resolve_tracker (Evoral::EventSink<samplepos_t>& buffer, samplepos_t time);
|
LIBARDOUR_API void resolve_tracker (Evoral::EventSink<samplepos_t>& buffer, samplepos_t time);
|
||||||
|
|
||||||
int use_playlist (DataType, std::shared_ptr<Playlist>);
|
LIBARDOUR_API int use_playlist (DataType, std::shared_ptr<Playlist>);
|
||||||
void playlist_ranges_moved (std::list<Temporal::RangeMove> const&, bool);
|
LIBARDOUR_API void playlist_ranges_moved (std::list<Temporal::RangeMove> const&, bool);
|
||||||
|
|
||||||
int add_channel_to (std::shared_ptr<ChannelList>, uint32_t how_many);
|
LIBARDOUR_API int add_channel_to (std::shared_ptr<ChannelList>, uint32_t how_many);
|
||||||
|
|
||||||
class DeclickAmp
|
class DeclickAmp
|
||||||
{
|
{
|
||||||
|
@ -229,9 +229,9 @@ private:
|
||||||
int channel,
|
int channel,
|
||||||
bool reversed);
|
bool reversed);
|
||||||
|
|
||||||
static Sample* _sum_buffer;
|
static thread_local Sample* _sum_buffer;
|
||||||
static Sample* _mixdown_buffer;
|
static thread_local Sample* _mixdown_buffer;
|
||||||
static gain_t* _gain_buffer;
|
static thread_local gain_t* _gain_buffer;
|
||||||
|
|
||||||
int refill (Sample* sum_buffer, Sample* mixdown_buffer, float* gain_buffer, samplecnt_t fill_level, bool reversed);
|
int refill (Sample* sum_buffer, Sample* mixdown_buffer, float* gain_buffer, samplecnt_t fill_level, bool reversed);
|
||||||
int refill_audio (Sample* sum_buffer, Sample* mixdown_buffer, float* gain_buffer, samplecnt_t fill_level, bool reversed);
|
int refill_audio (Sample* sum_buffer, Sample* mixdown_buffer, float* gain_buffer, samplecnt_t fill_level, bool reversed);
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ardour_io_tasklist_h_
|
||||||
|
#define _ardour_io_tasklist_h_
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <glibmm/threads.h>
|
||||||
|
|
||||||
|
#include "pbd/semutils.h"
|
||||||
|
|
||||||
|
#include "ardour/libardour_visibility.h"
|
||||||
|
|
||||||
|
namespace ARDOUR
|
||||||
|
{
|
||||||
|
|
||||||
|
class LIBARDOUR_API IOTaskList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IOTaskList (uint32_t);
|
||||||
|
~IOTaskList ();
|
||||||
|
|
||||||
|
/** process tasks in list in parallel, wait for them to complete */
|
||||||
|
void process ();
|
||||||
|
void push_back (boost::function<void ()> fn);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void* _worker_thread (void*);
|
||||||
|
|
||||||
|
void io_thread ();
|
||||||
|
|
||||||
|
std::vector<boost::function<void ()>> _tasks;
|
||||||
|
|
||||||
|
uint32_t _n_threads;
|
||||||
|
std::atomic<uint32_t> _n_workers;
|
||||||
|
std::vector<pthread_t> _workers;
|
||||||
|
std::atomic <bool> _terminate;
|
||||||
|
PBD::Semaphore _exec_sem;
|
||||||
|
PBD::Semaphore _idle_sem;
|
||||||
|
Glib::Threads::Mutex _tasks_mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ARDOUR
|
||||||
|
#endif
|
|
@ -222,6 +222,7 @@ CONFIG_VARIABLE (std::string, sample_lib_path, "sample-lib-path", "") /* custom
|
||||||
CONFIG_VARIABLE (bool, allow_special_bus_removal, "allow-special-bus-removal", false)
|
CONFIG_VARIABLE (bool, allow_special_bus_removal, "allow-special-bus-removal", false)
|
||||||
CONFIG_VARIABLE (int32_t, processor_usage, "processor-usage", -1)
|
CONFIG_VARIABLE (int32_t, processor_usage, "processor-usage", -1)
|
||||||
CONFIG_VARIABLE (int32_t, cpu_dma_latency, "cpu-dma-latency", -1) /* >=0 to enable */
|
CONFIG_VARIABLE (int32_t, cpu_dma_latency, "cpu-dma-latency", -1) /* >=0 to enable */
|
||||||
|
CONFIG_VARIABLE (int32_t, io_thread_count, "io-thread-count", -2)
|
||||||
CONFIG_VARIABLE (gain_t, max_gain, "max-gain", 2.0) /* +6.0dB */
|
CONFIG_VARIABLE (gain_t, max_gain, "max-gain", 2.0) /* +6.0dB */
|
||||||
CONFIG_VARIABLE (uint32_t, max_recent_sessions, "max-recent-sessions", 10)
|
CONFIG_VARIABLE (uint32_t, max_recent_sessions, "max-recent-sessions", 10)
|
||||||
CONFIG_VARIABLE (uint32_t, max_recent_templates, "max-recent-templates", 10)
|
CONFIG_VARIABLE (uint32_t, max_recent_templates, "max-recent-templates", 10)
|
||||||
|
|
|
@ -149,6 +149,7 @@ struct GraphChain;
|
||||||
class IO;
|
class IO;
|
||||||
class IOPlug;
|
class IOPlug;
|
||||||
class IOProcessor;
|
class IOProcessor;
|
||||||
|
class IOTaskList;
|
||||||
class ImportStatus;
|
class ImportStatus;
|
||||||
class MidiClockTicker;
|
class MidiClockTicker;
|
||||||
class MidiControlUI;
|
class MidiControlUI;
|
||||||
|
@ -334,6 +335,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<RTTaskList> rt_tasklist () { return _rt_tasklist; }
|
std::shared_ptr<RTTaskList> rt_tasklist () { return _rt_tasklist; }
|
||||||
|
std::shared_ptr<IOTaskList> io_tasklist () { return _io_tasklist; }
|
||||||
|
|
||||||
RouteList get_routelist (bool mixer_order = false, PresentationInfo::Flag fl = PresentationInfo::MixerRoutes) const;
|
RouteList get_routelist (bool mixer_order = false, PresentationInfo::Flag fl = PresentationInfo::MixerRoutes) const;
|
||||||
|
|
||||||
|
@ -2355,6 +2357,7 @@ private:
|
||||||
std::shared_ptr<Port> _ltc_output_port;
|
std::shared_ptr<Port> _ltc_output_port;
|
||||||
|
|
||||||
std::shared_ptr<RTTaskList> _rt_tasklist;
|
std::shared_ptr<RTTaskList> _rt_tasklist;
|
||||||
|
std::shared_ptr<IOTaskList> _io_tasklist;
|
||||||
|
|
||||||
/* Scene Changing */
|
/* Scene Changing */
|
||||||
SceneChanger* _scene_changer;
|
SceneChanger* _scene_changer;
|
||||||
|
|
|
@ -109,6 +109,7 @@ LIBARDOUR_API const char* native_header_format_extension (ARDOUR::HeaderFormat,
|
||||||
LIBARDOUR_API bool matching_unsuffixed_filename_exists_in (const std::string& dir, const std::string& name);
|
LIBARDOUR_API bool matching_unsuffixed_filename_exists_in (const std::string& dir, const std::string& name);
|
||||||
|
|
||||||
LIBARDOUR_API uint32_t how_many_dsp_threads ();
|
LIBARDOUR_API uint32_t how_many_dsp_threads ();
|
||||||
|
LIBARDOUR_API uint32_t how_many_io_threads ();
|
||||||
|
|
||||||
LIBARDOUR_API std::string compute_sha1_of_file (std::string path);
|
LIBARDOUR_API std::string compute_sha1_of_file (std::string path);
|
||||||
|
|
||||||
|
|
|
@ -669,9 +669,6 @@ AudioRegion::read_at (Sample* buf,
|
||||||
uint32_t fx_latency = _fx_latency;
|
uint32_t fx_latency = _fx_latency;
|
||||||
lm.release ();
|
lm.release ();
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 read at %3 - %4 to_read: %5 with fx: %6\n",
|
|
||||||
name(), chan_n, internal_offset, internal_offset + to_read, to_read, have_fx));
|
|
||||||
|
|
||||||
ChanCount cc (DataType::AUDIO, n_channels ());
|
ChanCount cc (DataType::AUDIO, n_channels ());
|
||||||
_readcache.ensure_buffers (cc, to_read + _fx_latency);
|
_readcache.ensure_buffers (cc, to_read + _fx_latency);
|
||||||
|
|
||||||
|
@ -698,6 +695,9 @@ AudioRegion::read_at (Sample* buf,
|
||||||
n_read = max<samplecnt_t> (0, min (to_read, lsamples - offset));
|
n_read = max<samplecnt_t> (0, min (to_read, lsamples - offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 read: %3 - %4 (%5) to_read: %6 offset: %7 with fx: %8 fx_latency: %9\n",
|
||||||
|
name(), chan_n, readat, readat + n_read, n_read, to_read, internal_offset, have_fx, fx_latency));
|
||||||
|
|
||||||
_readcache.ensure_buffers (cc, n_proc);
|
_readcache.ensure_buffers (cc, n_proc);
|
||||||
|
|
||||||
if (n_read < n_proc) {
|
if (n_read < n_proc) {
|
||||||
|
@ -2430,9 +2430,6 @@ AudioRegion::apply_region_fx (BufferSet& bufs, samplepos_t start_sample, samplep
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ARDOUR::ProcessThread* pt = new ProcessThread (); // TODO -> move to butler ?
|
|
||||||
pt->get_buffers ();
|
|
||||||
|
|
||||||
samplecnt_t latency_offset = 0;
|
samplecnt_t latency_offset = 0;
|
||||||
|
|
||||||
for (auto const& rfx : _plugins) {
|
for (auto const& rfx : _plugins) {
|
||||||
|
@ -2470,6 +2467,4 @@ AudioRegion::apply_region_fx (BufferSet& bufs, samplepos_t start_sample, samplep
|
||||||
}
|
}
|
||||||
_fx_pos = end_sample;
|
_fx_pos = end_sample;
|
||||||
_fx_latent_read = false;
|
_fx_latent_read = false;
|
||||||
pt->drop_buffers ();
|
|
||||||
delete pt;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,10 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_IOPRIO
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef PLATFORM_WINDOWS
|
#ifndef PLATFORM_WINDOWS
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -40,6 +44,7 @@
|
||||||
#include "ardour/disk_io.h"
|
#include "ardour/disk_io.h"
|
||||||
#include "ardour/disk_reader.h"
|
#include "ardour/disk_reader.h"
|
||||||
#include "ardour/io.h"
|
#include "ardour/io.h"
|
||||||
|
#include "ardour/io_tasklist.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
#include "ardour/track.h"
|
#include "ardour/track.h"
|
||||||
|
|
||||||
|
@ -167,7 +172,17 @@ Butler::_thread_work (void* arg)
|
||||||
{
|
{
|
||||||
SessionEvent::create_per_thread_pool ("butler events", 4096);
|
SessionEvent::create_per_thread_pool ("butler events", 4096);
|
||||||
pthread_set_name (X_("butler"));
|
pthread_set_name (X_("butler"));
|
||||||
return ((Butler*)arg)->thread_work ();
|
/* get thread buffers for RegionFx */
|
||||||
|
ARDOUR::ProcessThread* pt = new ProcessThread ();
|
||||||
|
pt->get_buffers ();
|
||||||
|
DiskReader::allocate_working_buffers ();
|
||||||
|
|
||||||
|
void* rv = ((Butler*)arg)->thread_work ();
|
||||||
|
|
||||||
|
DiskReader::free_working_buffers ();
|
||||||
|
pt->drop_buffers ();
|
||||||
|
delete pt;
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void*
|
void*
|
||||||
|
@ -177,6 +192,13 @@ Butler::thread_work ()
|
||||||
bool disk_work_outstanding = false;
|
bool disk_work_outstanding = false;
|
||||||
RouteList::iterator i;
|
RouteList::iterator i;
|
||||||
|
|
||||||
|
#ifdef HAVE_IOPRIO
|
||||||
|
// ioprio_set (IOPRIO_WHO_PROCESS, 0 /*calling thread*/, IOPRIO_PRIO_VALUE (IOPRIO_CLASS_RT, 4))
|
||||||
|
if (0 != syscall (SYS_ioprio_set, 1, 0, (1 << 13) | 4)) {
|
||||||
|
warning << _("Cannot set I/O Priority for disk read/write thread") << endmsg;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 butler main loop, disk work outstanding ? %2 @ %3\n", DEBUG_THREAD_SELF, disk_work_outstanding, g_get_monotonic_time ()));
|
DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 butler main loop, disk work outstanding ? %2 @ %3\n", DEBUG_THREAD_SELF, disk_work_outstanding, g_get_monotonic_time ()));
|
||||||
|
|
||||||
|
@ -253,6 +275,8 @@ Butler::thread_work ()
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::Butler, string_compose ("butler starts refill loop, twr = %1\n", transport_work_requested ()));
|
DEBUG_TRACE (DEBUG::Butler, string_compose ("butler starts refill loop, twr = %1\n", transport_work_requested ()));
|
||||||
|
|
||||||
|
std::shared_ptr<IOTaskList> tl = _session.io_tasklist ();
|
||||||
|
|
||||||
for (i = rl_with_auditioner.begin (); !transport_work_requested () && should_run && i != rl_with_auditioner.end (); ++i) {
|
for (i = rl_with_auditioner.begin (); !transport_work_requested () && should_run && i != rl_with_auditioner.end (); ++i) {
|
||||||
std::shared_ptr<Track> tr = std::dynamic_pointer_cast<Track> (*i);
|
std::shared_ptr<Track> tr = std::dynamic_pointer_cast<Track> (*i);
|
||||||
|
|
||||||
|
@ -267,24 +291,27 @@ Butler::thread_work ()
|
||||||
// DEBUG_TRACE (DEBUG::Butler, string_compose ("butler skips inactive track %1\n", tr->name()));
|
// DEBUG_TRACE (DEBUG::Butler, string_compose ("butler skips inactive track %1\n", tr->name()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// DEBUG_TRACE (DEBUG::Butler, string_compose ("butler refills %1, playback load = %2\n", tr->name(), tr->playback_buffer_load()));
|
|
||||||
switch (tr->do_refill ()) {
|
|
||||||
case 0:
|
|
||||||
//DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name()));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
tl->push_back ([tr, &disk_work_outstanding]() {
|
||||||
DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill unfinished %1\n", tr->name ()));
|
switch (tr->do_refill ()) {
|
||||||
disk_work_outstanding = true;
|
case 0:
|
||||||
break;
|
//DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name()));
|
||||||
|
break;
|
||||||
default:
|
case 1:
|
||||||
error << string_compose (_("Butler read ahead failure on dstream %1"), (*i)->name ()) << endmsg;
|
DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill unfinished %1\n", tr->name ()));
|
||||||
std::cerr << string_compose (_("Butler read ahead failure on dstream %1"), (*i)->name ()) << std::endl;
|
disk_work_outstanding = true;
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
|
error << string_compose (_("Butler read ahead failure on dstream %1"), tr->name ()) << endmsg;
|
||||||
|
std::cerr << string_compose (_("Butler read ahead failure on dstream %1"), tr->name ()) << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tl->process ();
|
||||||
|
tl.reset ();
|
||||||
|
|
||||||
if (i != rl_with_auditioner.begin () && i != rl_with_auditioner.end ()) {
|
if (i != rl_with_auditioner.begin () && i != rl_with_auditioner.end ()) {
|
||||||
/* we didn't get to all the streams */
|
/* we didn't get to all the streams */
|
||||||
disk_work_outstanding = true;
|
disk_work_outstanding = true;
|
||||||
|
|
|
@ -53,6 +53,7 @@ PBD::DebugBits PBD::DEBUG::FaderPort = PBD::new_debug_bit ("faderport");
|
||||||
PBD::DebugBits PBD::DEBUG::FaderPort8 = PBD::new_debug_bit ("faderport8");
|
PBD::DebugBits PBD::DEBUG::FaderPort8 = PBD::new_debug_bit ("faderport8");
|
||||||
PBD::DebugBits PBD::DEBUG::GenericMidi = PBD::new_debug_bit ("genericmidi");
|
PBD::DebugBits PBD::DEBUG::GenericMidi = PBD::new_debug_bit ("genericmidi");
|
||||||
PBD::DebugBits PBD::DEBUG::Graph = PBD::new_debug_bit ("graph");
|
PBD::DebugBits PBD::DEBUG::Graph = PBD::new_debug_bit ("graph");
|
||||||
|
PBD::DebugBits PBD::DEBUG::IOTaskList = PBD::new_debug_bit ("iotasklist");
|
||||||
PBD::DebugBits PBD::DEBUG::LTC = PBD::new_debug_bit ("ltc");
|
PBD::DebugBits PBD::DEBUG::LTC = PBD::new_debug_bit ("ltc");
|
||||||
PBD::DebugBits PBD::DEBUG::LV2 = PBD::new_debug_bit ("lv2");
|
PBD::DebugBits PBD::DEBUG::LV2 = PBD::new_debug_bit ("lv2");
|
||||||
PBD::DebugBits PBD::DEBUG::LV2Automate = PBD::new_debug_bit ("lv2automate");
|
PBD::DebugBits PBD::DEBUG::LV2Automate = PBD::new_debug_bit ("lv2automate");
|
||||||
|
|
|
@ -49,9 +49,9 @@ using namespace std;
|
||||||
|
|
||||||
ARDOUR::samplecnt_t DiskReader::_chunk_samples = default_chunk_samples ();
|
ARDOUR::samplecnt_t DiskReader::_chunk_samples = default_chunk_samples ();
|
||||||
PBD::Signal0<void> DiskReader::Underrun;
|
PBD::Signal0<void> DiskReader::Underrun;
|
||||||
Sample* DiskReader::_sum_buffer = 0;
|
thread_local Sample* DiskReader::_sum_buffer = 0;
|
||||||
Sample* DiskReader::_mixdown_buffer = 0;
|
thread_local Sample* DiskReader::_mixdown_buffer = 0;
|
||||||
gain_t* DiskReader::_gain_buffer = 0;
|
thread_local gain_t* DiskReader::_gain_buffer = 0;
|
||||||
std::atomic<int> DiskReader::_no_disk_output (0);
|
std::atomic<int> DiskReader::_no_disk_output (0);
|
||||||
DiskReader::Declicker DiskReader::loop_declick_in;
|
DiskReader::Declicker DiskReader::loop_declick_in;
|
||||||
DiskReader::Declicker DiskReader::loop_declick_out;
|
DiskReader::Declicker DiskReader::loop_declick_out;
|
||||||
|
|
|
@ -734,11 +734,14 @@ ARDOUR::init (bool try_optimization, const char* localedir, bool with_gui)
|
||||||
* each cycle). Session Export uses one, and the GUI requires
|
* each cycle). Session Export uses one, and the GUI requires
|
||||||
* buffers (for plugin-analysis, auditioner updates) but not
|
* buffers (for plugin-analysis, auditioner updates) but not
|
||||||
* concurrently.
|
* concurrently.
|
||||||
* Last but not least, the butler needs one for RegionFX.
|
|
||||||
*
|
*
|
||||||
* In theory (hw + 4) should be sufficient, let's add one for luck.
|
* Last but not least, the butler needs one for RegionFX for
|
||||||
|
* each I/O thread (up to hardware_concurrency) and one for itself
|
||||||
|
* (butler's main thread).
|
||||||
|
*
|
||||||
|
* In theory (2 * hw + 4) should be sufficient, let's add one for luck.
|
||||||
*/
|
*/
|
||||||
BufferManager::init (hardware_concurrency () + 5);
|
BufferManager::init (hardware_concurrency () * 2 + 5);
|
||||||
|
|
||||||
PannerManager::instance ().discover_panners ();
|
PannerManager::instance ().discover_panners ();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017-2024 Robin Gareus <robin@gareus.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_IOPRIO
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "pbd/compose.h"
|
||||||
|
#include "pbd/cpus.h"
|
||||||
|
#include "pbd/debug.h"
|
||||||
|
#include "pbd/failed_constructor.h"
|
||||||
|
#include "pbd/pthread_utils.h"
|
||||||
|
|
||||||
|
#include "temporal/tempo.h"
|
||||||
|
|
||||||
|
#include "ardour/debug.h"
|
||||||
|
#include "ardour/disk_reader.h"
|
||||||
|
#include "ardour/io_tasklist.h"
|
||||||
|
#include "ardour/process_thread.h"
|
||||||
|
#include "ardour/session_event.h"
|
||||||
|
|
||||||
|
using namespace ARDOUR;
|
||||||
|
|
||||||
|
IOTaskList::IOTaskList (uint32_t n_threads)
|
||||||
|
: _n_threads (n_threads)
|
||||||
|
, _terminate (false)
|
||||||
|
, _exec_sem ("io thread exec", 0)
|
||||||
|
, _idle_sem ("io thread idle", 0)
|
||||||
|
{
|
||||||
|
assert (n_threads <= hardware_concurrency ());
|
||||||
|
|
||||||
|
if (n_threads < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_attr_t attr;
|
||||||
|
struct sched_param parm;
|
||||||
|
parm.sched_priority = pbd_absolute_rt_priority (SCHED_RR, pbd_pthread_priority (THREAD_IO));
|
||||||
|
|
||||||
|
pthread_attr_init (&attr);
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
pthread_attr_setschedpolicy (&attr, SCHED_OTHER);
|
||||||
|
#else
|
||||||
|
pthread_attr_setschedpolicy (&attr, SCHED_RR);
|
||||||
|
#endif
|
||||||
|
pthread_attr_setschedparam (&attr, &parm);
|
||||||
|
pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
|
||||||
|
pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);
|
||||||
|
|
||||||
|
DEBUG_TRACE (PBD::DEBUG::IOTaskList, string_compose ("IOTaskList starting %1 threads with priority = %2\n", _n_threads, parm.sched_priority));
|
||||||
|
|
||||||
|
_workers.resize (_n_threads);
|
||||||
|
for (uint32_t i = 0; i < _n_threads; ++i) {
|
||||||
|
if (pthread_create (&_workers[i], &attr, &_worker_thread, this)) {
|
||||||
|
throw failed_constructor ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_attr_destroy (&attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
IOTaskList::~IOTaskList ()
|
||||||
|
{
|
||||||
|
_terminate.store (true);
|
||||||
|
for (size_t i = 0; i < _workers.size (); ++i) {
|
||||||
|
_exec_sem.signal ();
|
||||||
|
}
|
||||||
|
for (auto const& t : _workers) {
|
||||||
|
pthread_join (t, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
IOTaskList::push_back (boost::function<void ()> fn)
|
||||||
|
{
|
||||||
|
_tasks.push_back (fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
IOTaskList::process ()
|
||||||
|
{
|
||||||
|
assert (strcmp (pthread_name (), "butler") == 0);
|
||||||
|
if (_n_threads > 1 && _tasks.size () > 2) {
|
||||||
|
uint32_t wakeup = std::min<uint32_t> (_n_threads, _tasks.size ());
|
||||||
|
DEBUG_TRACE (PBD::DEBUG::IOTaskList, string_compose ("IOTaskList process wakeup %1 thread for %2 tasks.\n", wakeup, _tasks.size ()))
|
||||||
|
for (uint32_t i = 0; i < wakeup; ++i) {
|
||||||
|
_exec_sem.signal ();
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < wakeup; ++i) {
|
||||||
|
_idle_sem.wait ();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DEBUG_TRACE (PBD::DEBUG::IOTaskList, string_compose ("IOTaskList process %1 task(s) in main thread.\n", _tasks.size ()))
|
||||||
|
for (auto const& fn : _tasks) {
|
||||||
|
fn ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tasks.clear ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
IOTaskList::_worker_thread (void* me)
|
||||||
|
{
|
||||||
|
IOTaskList* self = static_cast<IOTaskList*> (me);
|
||||||
|
|
||||||
|
uint32_t id = self->_n_workers.fetch_add (1);
|
||||||
|
char name[64];
|
||||||
|
snprintf (name, 64, "IO-%u-%p", id, (void*)DEBUG_THREAD_SELF);
|
||||||
|
pthread_set_name (name);
|
||||||
|
|
||||||
|
SessionEvent::create_per_thread_pool (name, 64);
|
||||||
|
PBD::notify_event_loops_about_thread_creation (pthread_self (), name, 64);
|
||||||
|
|
||||||
|
DiskReader::allocate_working_buffers ();
|
||||||
|
ARDOUR::ProcessThread* pt = new ProcessThread ();
|
||||||
|
pt->get_buffers ();
|
||||||
|
|
||||||
|
#ifdef HAVE_IOPRIO
|
||||||
|
/* compare to Butler::_thread_work */
|
||||||
|
// ioprio_set (IOPRIO_WHO_PROCESS, 0 /*calling thread*/, IOPRIO_PRIO_VALUE (IOPRIO_CLASS_RT, 4))
|
||||||
|
syscall (SYS_ioprio_set, 1, 0, (1 << 13) | 4);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
self->io_thread ();
|
||||||
|
|
||||||
|
DiskReader::free_working_buffers ();
|
||||||
|
pt->drop_buffers ();
|
||||||
|
delete pt;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
IOTaskList::io_thread ()
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
_exec_sem.wait ();
|
||||||
|
if (_terminate.load ()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Temporal::TempoMap::fetch ();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
boost::function<void()> fn;
|
||||||
|
Glib::Threads::Mutex::Lock lm (_tasks_mutex);
|
||||||
|
if (_tasks.empty ()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fn = _tasks.back ();
|
||||||
|
_tasks.pop_back ();
|
||||||
|
lm.release ();
|
||||||
|
|
||||||
|
fn ();
|
||||||
|
}
|
||||||
|
_idle_sem.signal ();
|
||||||
|
}
|
||||||
|
}
|
|
@ -610,6 +610,7 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate)
|
||||||
static const int32_t _max_block_length = 8192; // max possible (with all engines and during export)
|
static const int32_t _max_block_length = 8192; // max possible (with all engines and during export)
|
||||||
static const int32_t rt_policy = PBD_SCHED_FIFO;
|
static const int32_t rt_policy = PBD_SCHED_FIFO;
|
||||||
static const int32_t rt_priority = pbd_absolute_rt_priority (PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority () - 1);
|
static const int32_t rt_priority = pbd_absolute_rt_priority (PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority () - 1);
|
||||||
|
static const int32_t hw_concurrency = how_many_dsp_threads ();
|
||||||
/* Consider updating max-block-size whenever the buffersize changes.
|
/* Consider updating max-block-size whenever the buffersize changes.
|
||||||
* It requires re-instantiating the plugin (which is a non-realtime operation),
|
* It requires re-instantiating the plugin (which is a non-realtime operation),
|
||||||
* so it should be done lightly and only for plugins that require it.
|
* so it should be done lightly and only for plugins that require it.
|
||||||
|
@ -632,6 +633,8 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate)
|
||||||
sizeof(int32_t), atom_Int, &rt_policy },
|
sizeof(int32_t), atom_Int, &rt_policy },
|
||||||
{ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://ardour.org/lv2/threads/#schedPriority"),
|
{ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://ardour.org/lv2/threads/#schedPriority"),
|
||||||
sizeof(int32_t), atom_Int, &rt_priority },
|
sizeof(int32_t), atom_Int, &rt_priority },
|
||||||
|
{ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://ardour.org/lv2/threads/#concurrency"),
|
||||||
|
sizeof(int32_t), atom_Int, &hw_concurrency },
|
||||||
{ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://lv2plug.in/ns/extensions/ui#backgroundColor"),
|
{ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://lv2plug.in/ns/extensions/ui#backgroundColor"),
|
||||||
sizeof(int32_t), atom_Int, &_ui_background_color },
|
sizeof(int32_t), atom_Int, &_ui_background_color },
|
||||||
{ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://lv2plug.in/ns/extensions/ui#foregroundColor"),
|
{ LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://lv2plug.in/ns/extensions/ui#foregroundColor"),
|
||||||
|
|
|
@ -87,6 +87,7 @@
|
||||||
#include "ardour/internal_return.h"
|
#include "ardour/internal_return.h"
|
||||||
#include "ardour/internal_send.h"
|
#include "ardour/internal_send.h"
|
||||||
#include "ardour/io_plug.h"
|
#include "ardour/io_plug.h"
|
||||||
|
#include "ardour/io_tasklist.h"
|
||||||
#include "ardour/luabindings.h"
|
#include "ardour/luabindings.h"
|
||||||
#include "ardour/lv2_plugin.h"
|
#include "ardour/lv2_plugin.h"
|
||||||
#include "ardour/midiport_manager.h"
|
#include "ardour/midiport_manager.h"
|
||||||
|
@ -594,6 +595,8 @@ Session::immediately_post_engine ()
|
||||||
_process_graph.reset (new Graph (*this));
|
_process_graph.reset (new Graph (*this));
|
||||||
_rt_tasklist.reset (new RTTaskList (_process_graph));
|
_rt_tasklist.reset (new RTTaskList (_process_graph));
|
||||||
|
|
||||||
|
_io_tasklist.reset (new IOTaskList (how_many_io_threads ()));
|
||||||
|
|
||||||
/* every time we reconnect, recompute worst case output latencies */
|
/* every time we reconnect, recompute worst case output latencies */
|
||||||
|
|
||||||
_engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this));
|
_engine.Running.connect_same_thread (*this, boost::bind (&Session::initialize_latencies, this));
|
||||||
|
@ -724,6 +727,8 @@ Session::destroy ()
|
||||||
_io_graph_chain[0].reset ();
|
_io_graph_chain[0].reset ();
|
||||||
_io_graph_chain[1].reset ();
|
_io_graph_chain[1].reset ();
|
||||||
|
|
||||||
|
_io_tasklist.reset ();
|
||||||
|
|
||||||
_butler->drop_references ();
|
_butler->drop_references ();
|
||||||
delete _butler;
|
delete _butler;
|
||||||
_butler = 0;
|
_butler = 0;
|
||||||
|
@ -775,8 +780,6 @@ Session::destroy ()
|
||||||
_bundles.flush ();
|
_bundles.flush ();
|
||||||
_io_plugins.flush ();
|
_io_plugins.flush ();
|
||||||
|
|
||||||
DiskReader::free_working_buffers();
|
|
||||||
|
|
||||||
/* tell everyone who is still standing that we're about to die */
|
/* tell everyone who is still standing that we're about to die */
|
||||||
drop_references ();
|
drop_references ();
|
||||||
|
|
||||||
|
|
|
@ -272,7 +272,6 @@ Session::post_engine_init ()
|
||||||
_engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this, true));
|
_engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this, true));
|
||||||
_engine.MidiSelectionPortsChanged.connect_same_thread (*this, boost::bind (&Session::rewire_midi_selection_ports, this));
|
_engine.MidiSelectionPortsChanged.connect_same_thread (*this, boost::bind (&Session::rewire_midi_selection_ports, this));
|
||||||
|
|
||||||
DiskReader::allocate_working_buffers();
|
|
||||||
refresh_disk_space ();
|
refresh_disk_space ();
|
||||||
|
|
||||||
/* we're finally ready to call set_state() ... all objects have
|
/* we're finally ready to call set_state() ... all objects have
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
#include "ardour/click.h"
|
#include "ardour/click.h"
|
||||||
#include "ardour/debug.h"
|
#include "ardour/debug.h"
|
||||||
#include "ardour/disk_reader.h"
|
#include "ardour/disk_reader.h"
|
||||||
|
#include "ardour/io_tasklist.h"
|
||||||
#include "ardour/location.h"
|
#include "ardour/location.h"
|
||||||
#include "ardour/playlist.h"
|
#include "ardour/playlist.h"
|
||||||
#include "ardour/profile.h"
|
#include "ardour/profile.h"
|
||||||
|
@ -1151,14 +1152,17 @@ Session::butler_transport_work (bool have_process_lock)
|
||||||
if (!have_process_lock) {
|
if (!have_process_lock) {
|
||||||
lx.acquire ();
|
lx.acquire ();
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<IOTaskList> tl = io_tasklist ();
|
||||||
for (auto const& i : *r) {
|
for (auto const& i : *r) {
|
||||||
std::shared_ptr<Track> tr = std::dynamic_pointer_cast<Track> (i);
|
std::shared_ptr<Track> tr = std::dynamic_pointer_cast<Track> (i);
|
||||||
if (tr) {
|
if (tr) {
|
||||||
tr->adjust_playback_buffering ();
|
tr->adjust_playback_buffering ();
|
||||||
/* and refill those buffers ... */
|
/* and refill those buffers ... */
|
||||||
}
|
}
|
||||||
i->non_realtime_locate (_transport_sample);
|
tl->push_back ([this, i]() { i->non_realtime_locate (_transport_sample); });
|
||||||
}
|
}
|
||||||
|
tl->process ();
|
||||||
|
|
||||||
VCAList v = _vca_manager->vcas ();
|
VCAList v = _vca_manager->vcas ();
|
||||||
for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
|
for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
|
||||||
(*i)->non_realtime_locate (_transport_sample);
|
(*i)->non_realtime_locate (_transport_sample);
|
||||||
|
@ -1283,12 +1287,14 @@ Session::non_realtime_locate ()
|
||||||
tf = _transport_sample;
|
tf = _transport_sample;
|
||||||
start = get_microseconds ();
|
start = get_microseconds ();
|
||||||
|
|
||||||
|
std::shared_ptr<IOTaskList> tl = io_tasklist ();
|
||||||
for (auto const& i : *rl) {
|
for (auto const& i : *rl) {
|
||||||
++nt;
|
++nt;
|
||||||
i->non_realtime_locate (tf);
|
tl->push_back ([this, i, tf, sc]() { if (sc == _seek_counter.load ()) { i->non_realtime_locate (tf); }});
|
||||||
if (sc != _seek_counter.load ()) {
|
}
|
||||||
goto restart;
|
tl->process ();
|
||||||
}
|
if (sc != _seek_counter.load ()) {
|
||||||
|
goto restart;
|
||||||
}
|
}
|
||||||
|
|
||||||
microseconds_t end = get_microseconds ();
|
microseconds_t end = get_microseconds ();
|
||||||
|
@ -1528,15 +1534,26 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished, bool will_
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: locate\n"));
|
DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: locate\n"));
|
||||||
|
|
||||||
for (auto const& i : *r) {
|
std::shared_ptr<IOTaskList> tl = io_tasklist ();
|
||||||
DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler PTW: locate on %1\n", i->name()));
|
|
||||||
i->non_realtime_locate (_transport_sample);
|
|
||||||
|
|
||||||
if (on_entry != _butler->should_do_transport_work.load()) {
|
std::atomic<bool> fini (finished);
|
||||||
finished = false;
|
for (auto const& i : *r) {
|
||||||
/* we will be back */
|
tl->push_back ([this, i, on_entry, &fini]() {
|
||||||
return;
|
if (!fini.load ()) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler PTW: locate on %1\n", i->name()));
|
||||||
|
i->non_realtime_locate (_transport_sample);
|
||||||
|
if (on_entry != _butler->should_do_transport_work.load()) {
|
||||||
|
fini = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tl->process ();
|
||||||
|
if (!fini.load ()) {
|
||||||
|
finished = false;
|
||||||
|
/* we will be back */
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VCAList v = _vca_manager->vcas ();
|
VCAList v = _vca_manager->vcas ();
|
||||||
|
|
|
@ -696,6 +696,25 @@ ARDOUR::how_many_dsp_threads ()
|
||||||
return num_threads;
|
return num_threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
ARDOUR::how_many_io_threads ()
|
||||||
|
{
|
||||||
|
int num_cpu = hardware_concurrency();
|
||||||
|
int pu = Config->get_io_thread_count ();
|
||||||
|
uint32_t num_threads = max (num_cpu - 2, 2);
|
||||||
|
if (pu < 0) {
|
||||||
|
if (-pu < num_cpu) {
|
||||||
|
num_threads = num_cpu + pu;
|
||||||
|
}
|
||||||
|
} else if (pu == 0) {
|
||||||
|
num_threads = num_cpu;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
num_threads = min (num_cpu, pu);
|
||||||
|
}
|
||||||
|
return num_threads;
|
||||||
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
ARDOUR::gain_to_slider_position_with_max (double g, double max_gain)
|
ARDOUR::gain_to_slider_position_with_max (double g, double max_gain)
|
||||||
{
|
{
|
||||||
|
|
|
@ -110,6 +110,7 @@ libardour_sources = [
|
||||||
'io.cc',
|
'io.cc',
|
||||||
'io_plug.cc',
|
'io_plug.cc',
|
||||||
'io_processor.cc',
|
'io_processor.cc',
|
||||||
|
'io_tasklist.cc',
|
||||||
'kmeterdsp.cc',
|
'kmeterdsp.cc',
|
||||||
'ladspa_plugin.cc',
|
'ladspa_plugin.cc',
|
||||||
'latent.cc',
|
'latent.cc',
|
||||||
|
@ -345,9 +346,19 @@ def configure(conf):
|
||||||
|
|
||||||
conf.check(header_name='sys/vfs.h', define_name='HAVE_SYS_VFS_H',mandatory=False)
|
conf.check(header_name='sys/vfs.h', define_name='HAVE_SYS_VFS_H',mandatory=False)
|
||||||
conf.check(header_name='sys/statvfs.h', define_name='HAVE_SYS_STATVFS_H',mandatory=False)
|
conf.check(header_name='sys/statvfs.h', define_name='HAVE_SYS_STATVFS_H',mandatory=False)
|
||||||
|
|
||||||
conf.check(header_name='unistd.h', define_name='HAVE_UNISTD',mandatory=False)
|
conf.check(header_name='unistd.h', define_name='HAVE_UNISTD',mandatory=False)
|
||||||
|
|
||||||
|
have_sys_ioprio = conf.check_cc(
|
||||||
|
msg="Checking for 'ioprio_set' syscall support",
|
||||||
|
features = 'c',
|
||||||
|
mandatory = False,
|
||||||
|
execute = False,
|
||||||
|
fragment = "#include <sys/syscall.h>\nint main () { syscall(SYS_ioprio_set, 1, 0, 8192); return 0; }")
|
||||||
|
|
||||||
|
if have_sys_ioprio:
|
||||||
|
conf.define('HAVE_IOPRIO', 1)
|
||||||
|
conf.env['HAVE_IOPRIO'] = True
|
||||||
|
|
||||||
conf.write_config_header('libardour-config.h', remove=False)
|
conf.write_config_header('libardour-config.h', remove=False)
|
||||||
|
|
||||||
# Boost headers
|
# Boost headers
|
||||||
|
|
|
@ -68,7 +68,8 @@ LIBPBD_API void pthread_set_name (const char* name);
|
||||||
enum PBDThreadClass {
|
enum PBDThreadClass {
|
||||||
THREAD_MAIN, // main audio I/O thread
|
THREAD_MAIN, // main audio I/O thread
|
||||||
THREAD_MIDI, // MIDI I/O threads
|
THREAD_MIDI, // MIDI I/O threads
|
||||||
THREAD_PROC // realtime worker
|
THREAD_PROC, // realtime worker
|
||||||
|
THREAD_IO // non-realtime I/O
|
||||||
};
|
};
|
||||||
|
|
||||||
LIBPBD_API int pbd_pthread_priority (PBDThreadClass);
|
LIBPBD_API int pbd_pthread_priority (PBDThreadClass);
|
||||||
|
|
|
@ -280,6 +280,11 @@ pbd_pthread_priority (PBDThreadClass which)
|
||||||
default:
|
default:
|
||||||
case THREAD_PROC:
|
case THREAD_PROC:
|
||||||
return -2;
|
return -2;
|
||||||
|
case THREAD_IO:
|
||||||
|
/* https://github.com/mingw-w64/mingw-w64/blob/master/mingw-w64-libraries/winpthreads/src/sched.c
|
||||||
|
* -> THREAD_PRIORITY_HIGHEST
|
||||||
|
*/
|
||||||
|
return -13;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int base = -20;
|
int base = -20;
|
||||||
|
@ -299,6 +304,8 @@ pbd_pthread_priority (PBDThreadClass which)
|
||||||
default:
|
default:
|
||||||
case THREAD_PROC:
|
case THREAD_PROC:
|
||||||
return base - 2;
|
return base - 2;
|
||||||
|
case THREAD_IO:
|
||||||
|
return base - 10;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -1509,6 +1509,39 @@ icon_meters (cairo_t* cr, const int width, const int height, const uint32_t fg_c
|
||||||
VECTORICONSTROKE (lw, fg_color);
|
VECTORICONSTROKE (lw, fg_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
icon_waveform (cairo_t* cr, const int width, const int height, const uint32_t fg_color)
|
||||||
|
{
|
||||||
|
const double x = width * .5;
|
||||||
|
const double y = height * .5;
|
||||||
|
const double wh = std::min (x, y);
|
||||||
|
|
||||||
|
const double lw = DEFAULT_LINE_WIDTH;
|
||||||
|
const double lc = fmod (lw * .5, 1.0);
|
||||||
|
|
||||||
|
const int m = floor (1.6 * wh - lw);
|
||||||
|
const double x0 = rint (x + 1 - 0.5 * m);
|
||||||
|
|
||||||
|
// for i=0,60 do print (string.format ("%.2f, ", (math.random(1,100)/100) * (math.random(1,100)/100))) end
|
||||||
|
static const float wave[] = {
|
||||||
|
0.12, 0.40, 0.28, 0.21, 0.25, 0.57, 0.57, 0.41, 0.33, 0.63,
|
||||||
|
0.11, 0.89, 0.13, 0.29, 0.18, 0.24, 0.10, 0.05, 0.24, 0.15,
|
||||||
|
0.01, 0.39, 0.93, 0.27, 0.28, 0.07, 0.15, 0.12, 0.10, 0.13,
|
||||||
|
0.08, 0.03, 0.04, 0.59, 0.64, 0.49, 0.01, 0.04, 0.01, 0.39,
|
||||||
|
0.44, 0.01, 0.21, 0.12, 0.06, 0.07, 0.01, 0.11, 0.07, 0.33,
|
||||||
|
0.38, 0.24, 0.16, 0.64, 0.17, 0.05, 0.24, 0.07, 0.04, 0.35,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int p = sizeof(wave)/sizeof (float);
|
||||||
|
|
||||||
|
for (int i = 0; i < m; ++i) {
|
||||||
|
double dy = (wh * .8) * wave[i % p] * sqrt(sin (M_PI * i / m));
|
||||||
|
cairo_move_to (cr, x0 + i - lc, y - dy);
|
||||||
|
cairo_line_to (cr, x0 + i - lc, y + dy);
|
||||||
|
}
|
||||||
|
VECTORICONSTROKE (lw, fg_color);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -1667,6 +1700,9 @@ ArdourWidgets::ArdourIcon::render (cairo_t* cr
|
||||||
case Meters:
|
case Meters:
|
||||||
icon_meters (cr, width, height, fg_color);
|
icon_meters (cr, width, height, fg_color);
|
||||||
break;
|
break;
|
||||||
|
case TrackWaveform:
|
||||||
|
icon_waveform (cr, width, height, fg_color);
|
||||||
|
break;
|
||||||
case NoIcon:
|
case NoIcon:
|
||||||
rv = false;
|
rv = false;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -76,6 +76,7 @@ namespace ArdourWidgets { namespace ArdourIcon {
|
||||||
Lock,
|
Lock,
|
||||||
Mixer,
|
Mixer,
|
||||||
Meters,
|
Meters,
|
||||||
|
TrackWaveform,
|
||||||
NoIcon //< Last
|
NoIcon //< Last
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
1
wscript
1
wscript
|
@ -1583,6 +1583,7 @@ const char* const ardour_config_info = "\\n\\
|
||||||
write_config_text('Futex Semaphore', conf.is_defined('USE_FUTEX_SEMAPHORE'))
|
write_config_text('Futex Semaphore', conf.is_defined('USE_FUTEX_SEMAPHORE'))
|
||||||
write_config_text('Freedesktop files', opts.freedesktop)
|
write_config_text('Freedesktop files', opts.freedesktop)
|
||||||
write_config_text('G_ENABLE_DEBUG', opts.gdebug or conf.env['DEBUG'])
|
write_config_text('G_ENABLE_DEBUG', opts.gdebug or conf.env['DEBUG'])
|
||||||
|
write_config_text('I/O Priorty Set', conf.is_defined('HAVE_IOPRIO'))
|
||||||
write_config_text('Libjack linking', conf.env['libjack_link'])
|
write_config_text('Libjack linking', conf.env['libjack_link'])
|
||||||
write_config_text('Libjack metadata', conf.is_defined ('HAVE_JACK_METADATA'))
|
write_config_text('Libjack metadata', conf.is_defined ('HAVE_JACK_METADATA'))
|
||||||
write_config_text('Lua Binding Doc', conf.is_defined('LUABINDINGDOC'))
|
write_config_text('Lua Binding Doc', conf.is_defined('LUABINDINGDOC'))
|
||||||
|
|
Loading…
Reference in New Issue