From 09e9d7781b2b984caf3b7d85a755028031e81425 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 17 Sep 2024 00:01:51 +0200 Subject: [PATCH 01/75] Add build support for macOS/Sonoma At some point in the not-too-distant-future we need to simplify this and simply require Mojave/XCode 10 or later. --- wscript | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/wscript b/wscript index 79bf4cb9cc..36972a75c1 100644 --- a/wscript +++ b/wscript @@ -471,6 +471,8 @@ int main() { return 0; }''', conf.env['build_host'] = 'ventura' elif re.search ("^23[.]", version) is not None: conf.env['build_host'] = 'sonoma' + elif re.search ("^24[.]", version) is not None: + conf.env['build_host'] = 'sequoia' else: conf.env['build_host'] = 'irrelevant' @@ -512,6 +514,8 @@ int main() { return 0; }''', conf.env['build_target'] = 'ventura' elif re.search ("^23[.]", version) is not None: conf.env['build_target'] = 'sonoma' + elif re.search ("^24[.]", version) is not None: + conf.env['build_target'] = 'sequoia' else: conf.env['build_target'] = 'catalina' else: @@ -572,20 +576,20 @@ int main() { return 0; }''', elif conf.options.cxx11: conf.check_cxx(cxxflags=["-std=c++11"]) cxx_flags.append('-std=c++11') - elif conf.env['build_host'] in [ 'bigsur', 'monterey', 'ventura', 'sonoma' ]: + elif conf.env['build_host'] in [ 'bigsur', 'monterey', 'ventura', 'sonoma', 'sequoia' ]: conf.check_cxx(cxxflags=["-std=c++17"]) cxx_flags.append('-std=c++17') elif conf.env['build_host'] in [ 'mavericks', 'yosemite', 'el_capitan', 'sierra', 'high_sierra', 'mojave', 'catalina' ]: conf.check_cxx(cxxflags=["-std=c++11"]) cxx_flags.append('-std=c++11') - if conf.options.cxx11 or conf.options.cxx17 or conf.env['build_host'] in [ 'mavericks', 'yosemite', 'el_capitan', 'sierra', 'high_sierra', 'mojave', 'catalina' , 'bigsur', 'monterey', 'ventura', 'sonoma' ]: + if conf.options.cxx11 or conf.options.cxx17 or conf.env['build_host'] in [ 'mavericks', 'yosemite', 'el_capitan', 'sierra', 'high_sierra', 'mojave', 'catalina' , 'bigsur', 'monterey', 'ventura', 'sonoma', 'sequoia' ]: if platform == "darwin": # Mavericks and later changed the syntax to be used when including Carbon headers, # from requiring a full path to requiring just the header name. cxx_flags.append('-DCARBON_FLAT_HEADERS') - if not opt.use_libcpp and not conf.env['build_host'] in [ 'yosemite', 'el_capitan', 'sierra', 'high_sierra', 'mojave', 'catalina', 'bigsur', 'monterey', 'ventura', 'sonoma' ]: + if not opt.use_libcpp and not conf.env['build_host'] in [ 'yosemite', 'el_capitan', 'sierra', 'high_sierra', 'mojave', 'catalina', 'bigsur', 'monterey', 'ventura', 'sonoma', 'sequoia' ]: cxx_flags.append('--stdlib=libstdc++') linker_flags.append('--stdlib=libstdc++') # Prevents visibility issues in standard headers @@ -597,7 +601,7 @@ int main() { return 0; }''', cxx_flags.append('-DBOOST_NO_AUTO_PTR') cxx_flags.append('-DBOOST_BIND_GLOBAL_PLACEHOLDERS') - if (is_clang and platform == "darwin") or conf.env['build_host'] in [ 'mavericks', 'yosemite', 'el_capitan', 'sierra', 'high_sierra', 'mojave', 'catalina' , 'bigsur', 'monterey', 'ventura', 'sonoma' ]: + if (is_clang and platform == "darwin") or conf.env['build_host'] in [ 'mavericks', 'yosemite', 'el_capitan', 'sierra', 'high_sierra', 'mojave', 'catalina' , 'bigsur', 'monterey', 'ventura', 'sonoma', 'sequoia' ]: # Silence warnings about the non-existing osx clang compiler flags # -compatibility_version and -current_version. These are Waf # generated and not needed with clang @@ -727,7 +731,7 @@ int main() { return 0; }''', "-mmacosx-version-min=10.11")) linker_flags.append("-mmacosx-version-min=10.11") - elif conf.env['build_target'] in ['bigsur', 'monterey', 'ventura', 'sonoma']: + elif conf.env['build_target'] in ['bigsur', 'monterey', 'ventura', 'sonoma', 'sequoia']: compiler_flags.extend( ("-DMAC_OS_X_VERSION_MAX_ALLOWED=110000", "-mmacosx-version-min=11.0")) @@ -1509,7 +1513,7 @@ int main () { __int128 x = 0; return 0; } set_compiler_flags (conf, Options.options) if sys.platform == 'darwin': - if conf.env['build_host'] not in [ 'mojave', 'catalina', 'bigsur', 'monterey', 'ventura', 'sonoma']: + if conf.env['build_host'] not in [ 'mojave', 'catalina', 'bigsur', 'monterey', 'ventura', 'sonoma', 'sequoia']: conf.env.append_value('CXXFLAGS_OSX', '-F/System/Library/Frameworks') conf.env.append_value('CXXFLAGS_OSX', '-F/Library/Frameworks') From 8ab7c05382bdb39e40a5179c989b9663e93fa302 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 17 Sep 2024 22:16:28 +0200 Subject: [PATCH 02/75] Fix fan-out of instruments with illegal path-chars in their port-name e.g. EZDrummer "1/2" (or generic "L/R"). While those names are perfectly fine for Ports, tracks (file) names cannot include those chars. Since fan-out looks up routes by name this failed to properly connect stereo pinouts since 4a14f2fed5, 5b746b186. --- gtk2_ardour/mixer_ui.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gtk2_ardour/mixer_ui.cc b/gtk2_ardour/mixer_ui.cc index 94cc13b305..91c37f88c2 100644 --- a/gtk2_ardour/mixer_ui.cc +++ b/gtk2_ardour/mixer_ui.cc @@ -59,6 +59,7 @@ #include "ardour/route_group.h" #include "ardour/selection.h" #include "ardour/session.h" +#include "ardour/utils.h" #include "ardour/vca.h" #include "ardour/vca_manager.h" @@ -1026,7 +1027,7 @@ Mixer_UI::fan_out (std::weak_ptr wr, bool to_busses, bool group) return; } -#define BUSNAME pd.group_name + "(" + route->name () + ")" +#define BUSNAME legalize_for_universal_path (pd.group_name + " (" + route->name () + ")") /* count busses and channels/bus */ std::shared_ptr plugin = pi->plugin (); From 57a67738335c8d055599c5792d014bc6d7c6bc21 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Wed, 18 Sep 2024 15:27:22 +0200 Subject: [PATCH 03/75] Check compiler version to set compiler warning options This cleans up nightly build log (clang 13) and allows clean compiler output on dev systems (clang 16). --- wscript | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/wscript b/wscript index 36972a75c1..ff547a80d1 100644 --- a/wscript +++ b/wscript @@ -150,7 +150,7 @@ clang_dict['xsaveintrin'] = '' clang_dict['xmmintrinsics'] = '' clang_dict['silence-unused-arguments'] = '-Qunused-arguments' clang_dict['extra-cxx-warnings'] = [ '-Woverloaded-virtual', '-Wno-mismatched-tags', '-Wno-cast-align', '-Wno-unused-local-typedefs', '-Wunneeded-internal-declaration' ] -clang_dict['basic-warnings'] = [ '-Wall', '-Wpointer-arith', '-Wcast-qual', '-Wcast-align', '-Wno-unused-parameter', '-Wno-deprecated-declarations', '-Wno-deprecated-copy-with-user-provided-copy' ] +clang_dict['basic-warnings'] = [ '-Wall', '-Wpointer-arith', '-Wcast-qual', '-Wcast-align', '-Wno-unused-parameter', '-Wno-deprecated-declarations' ] clang_dict['cxx-strict'] = [ '-ansi', '-Wnon-virtual-dtor', '-Woverloaded-virtual', '-fstrict-overflow' ] clang_dict['strict'] = ['-Wall', '-Wcast-align', '-Wextra', '-Wwrite-strings' ] clang_dict['generic-x86'] = [ '-arch', 'i386' ] @@ -163,6 +163,12 @@ clang_darwin_dict['cxx-strict'] = [ '-ansi', '-Wnon-virtual-dtor', '-Woverloaded clang_darwin_dict['full-optimization'] = [ '-O3', '-ffast-math'] compiler_flags_dictionaries['clang-darwin'] = clang_darwin_dict +# Xcode 15 does not like our boost version, producing warnings from almost every file +clang15_darwin_dict = compiler_flags_dictionaries['clang-darwin'].copy() +clang15_darwin_dict['basic-warnings'].append ("-Wno-deprecated-builtins") +clang15_darwin_dict['basic-warnings'].append ("-Wno-deprecated-copy-with-user-provided-copy") +compiler_flags_dictionaries['clang15-darwin'] = clang15_darwin_dict + # Version stuff def fetch_git_revision_date (): @@ -408,9 +414,25 @@ int main() { return 0; }''', execute = False, msg = 'Checking for clang') + + if platform == 'darwin' and is_clang: + is_clang15_darwin = conf.check_cxx(fragment = ''' +#if !defined __clang_major__ || __clang_major__ < 15 +#error +#endif +int main() { return 0; }''', + features = 'cxx', + mandatory = False, + execute = False, + msg = 'Checking for clang >= 15') + + if is_clang: if platform == 'darwin': - compiler_name = 'clang-darwin' + if is_clang15_darwin: + compiler_name = 'clang15-darwin' + else: + compiler_name = 'clang-darwin' else: compiler_name = 'clang' elif conf.env['MSVC_COMPILER']: @@ -736,9 +758,6 @@ int main() { return 0; }''', ("-DMAC_OS_X_VERSION_MAX_ALLOWED=110000", "-mmacosx-version-min=11.0")) linker_flags.append("-mmacosx-version-min=11.0") - # Xcode 15 does not like our boost version, producing warnings from almost every file - # boost/type_traits/has_trivial_destructor.hpp:30:86: warning: builtin __has_trivial_destructor is deprecated; use __is_trivially_destructible instead - flags_dict['basic-warnings'].append ("-Wno-deprecated-builtins") # # save off CPU element in an env From adf511264b54792326251b1367ec675b67f76fa7 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 19 Sep 2024 17:24:00 +0200 Subject: [PATCH 04/75] NO-OP: sort dark color names alphabetically This is how Ardour's libXML saves the color files and it is easier to diff files. --- gtk2_ardour/themes/dark-ardour.colors | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/gtk2_ardour/themes/dark-ardour.colors b/gtk2_ardour/themes/dark-ardour.colors index 10d015df15..f5485edbf0 100644 --- a/gtk2_ardour/themes/dark-ardour.colors +++ b/gtk2_ardour/themes/dark-ardour.colors @@ -178,12 +178,12 @@ + - @@ -300,16 +300,19 @@ + + + + + + - - - @@ -355,14 +358,15 @@ - + + @@ -406,8 +410,6 @@ - - @@ -437,7 +439,6 @@ - @@ -478,9 +479,8 @@ - - - + + From 66fdee2e457ffe5d5d18440b95397394822edf3c Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 22 Sep 2024 16:12:13 +0200 Subject: [PATCH 05/75] Require user's color theme files to be versioned Previously tagged releases did not append major.minor version to a user's "my-*.colors" file. Ardour also loaded theme files which a user saved with a previous version. When new colors were added, those are missing from the older my-*.colors file. see also https://discourse.ardour.org/t/color-theme-issues-with-ardour-8-7/110729/20 --- gtk2_ardour/ui_config.cc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/gtk2_ardour/ui_config.cc b/gtk2_ardour/ui_config.cc index 428707e78f..0dbc8b9d04 100644 --- a/gtk2_ardour/ui_config.cc +++ b/gtk2_ardour/ui_config.cc @@ -315,7 +315,7 @@ UIConfiguration::color_file_name (bool use_my, bool with_version, bool fallback) std::string rev (revision); std::size_t pos = rev.find_first_of("-"); - if (with_version && pos != string::npos && pos > 0) { + if (with_version && pos > 0) { basename += "-"; basename += rev.substr (0, pos); // COLORFILE_VERSION - program major.minor } @@ -364,12 +364,6 @@ UIConfiguration::load_color_theme (bool allow_own) if (find_file (sp, color_file_name (true, true), cfile)) { found = true; } - - if (!found) { - if (find_file (sp, color_file_name (true, false), cfile)) { - found = true; - } - } } /* now search for a versioned color file (for this major.minor version) CURRENTLY UNUSED */ @@ -439,7 +433,7 @@ UIConfiguration::store_color_theme () root->add_child_nocopy (*parent); XMLTree tree; - std::string colorfile = Glib::build_filename (user_config_directory(), color_file_name (true, true));; + std::string colorfile = Glib::build_filename (user_config_directory(), color_file_name (true, true)); tree.set_root (root); From f75f5b0ded2017aebc991f4e864d99df3338cc2e Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Sun, 22 Sep 2024 09:49:41 -0600 Subject: [PATCH 06/75] add option to name new MIDI tracks from SMF with just the SMF track name (libs edition) --- libs/ardour/ardour/session.h | 2 +- libs/ardour/ardour/types.h | 1 + libs/ardour/import.cc | 18 +++++++++++++----- libs/ardour/luabindings.cc | 1 + 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index cb16cb3c5e..ebf15513a2 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -299,7 +299,7 @@ public: RouteList new_route_from_template (uint32_t how_many, PresentationInfo::order_t insert_at, const std::string& template_path, const std::string& name, PlaylistDisposition pd = NewPlaylist); RouteList new_route_from_template (uint32_t how_many, PresentationInfo::order_t insert_at, XMLNode&, const std::string& name, PlaylistDisposition pd = NewPlaylist); std::vector get_paths_for_new_sources (bool allow_replacing, const std::string& import_file_path, - uint32_t channels, std::vector const & smf_track_names); + uint32_t channels, std::vector const & smf_track_names, bool use_smf_file_names); int bring_all_sources_into_session (boost::function callback); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 13103f0584..5e20adc914 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -854,6 +854,7 @@ enum PlaylistDisposition { enum MidiTrackNameSource { SMFTrackNumber, SMFTrackName, + SMFFileAndTrackName, SMFInstrumentName }; diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 2759179990..640aa7ee64 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -144,7 +144,7 @@ open_importable_source (const string& path, samplecnt_t samplerate, ARDOUR::SrcQ vector Session::get_paths_for_new_sources (bool /*allow_replacing*/, const string& import_file_path, uint32_t channels, - vector const & smf_names) + vector const & smf_names, bool use_smf_file_names) { vector new_paths; @@ -159,8 +159,12 @@ Session::get_paths_for_new_sources (bool /*allow_replacing*/, const string& impo case DataType::MIDI: if (channels > 1) { assert (smf_names.size() == channels); - string mchn_name = string_compose ("%1.%2", basename, smf_names[n]); - filepath = new_midi_source_path (mchn_name); + if (use_smf_file_names) { + string mchn_name = string_compose ("%1.%2", basename, smf_names[n]); + filepath = new_midi_source_path (mchn_name); + } else { + filepath = new_midi_source_path (smf_names[n]); + } } else { filepath = new_midi_source_path (basename); } @@ -552,7 +556,7 @@ Session::deinterlace_midi_region (std::shared_ptr mr) for (int i = 0; i<16; i++) { smf_names.push_back(string_compose("-ch%1", i+1)); } - vector new_paths = get_paths_for_new_sources (false, source_path, 16, smf_names); + vector new_paths = get_paths_for_new_sources (false, source_path, 16, smf_names, true); /* create source files and write 1 channel of midi data to each of them */ if (create_mono_sources_for_writing (new_paths, *this, sample_rate(), newfiles, 0, false)) { @@ -635,6 +639,7 @@ Session::import_files (ImportStatus& status) std::shared_ptr smfs; uint32_t num_channels = 0; vector smf_names; + bool smf_keep_filename = false; status.sources.clear (); @@ -689,6 +694,9 @@ Session::import_files (ImportStatus& status) } } break; + case SMFFileAndTrackName: + smf_keep_filename = true; + /*FALLTHRU*/ case SMFTrackName: if (status.split_midi_channels) { vector temp; @@ -728,7 +736,7 @@ Session::import_files (ImportStatus& status) continue; } - vector new_paths = get_paths_for_new_sources (status.replace_existing_source, *p, num_channels, smf_names); + vector new_paths = get_paths_for_new_sources (status.replace_existing_source, *p, num_channels, smf_names, smf_keep_filename); Sources newfiles; samplepos_t natural_position = source ? source->natural_position() : 0; diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index 73af0af10d..5a6dc3b772 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -2571,6 +2571,7 @@ LuaBindings::common (lua_State* L) .beginNamespace ("MidiTrackNameSource") .addConst ("SMFTrackNumber", ARDOUR::MidiTrackNameSource(SMFTrackNumber)) .addConst ("SMFTrackName", ARDOUR::MidiTrackNameSource(SMFTrackName)) + .addConst ("SMFFileAndTrackName", ARDOUR::MidiTrackNameSource(SMFFileAndTrackName)) .addConst ("SMFInstrumentName", ARDOUR::MidiTrackNameSource(SMFInstrumentName)) .endNamespace () From 61988bb032dd4a4fbb6e418e2e63b3ea16776a24 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Sun, 22 Sep 2024 09:49:47 -0600 Subject: [PATCH 07/75] add option to name new MIDI tracks from SMF with just the SMF track name (gui editing) --- gtk2_ardour/sfdb_ui.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc index 52731fc6d9..c6ca2a32fc 100644 --- a/gtk2_ardour/sfdb_ui.cc +++ b/gtk2_ardour/sfdb_ui.cc @@ -111,6 +111,8 @@ string2miditracknamesource (string const & str) return SMFTrackNumber; } else if (str == _("by track name")) { return SMFTrackName; + } else if (str == _("by file and track name")) { + return SMFFileAndTrackName; } else if (str == _("by instrument name")) { return SMFInstrumentName; } @@ -1981,6 +1983,7 @@ SoundFileOmega::SoundFileOmega (string title, ARDOUR::Session* s, str.clear (); str.push_back (_("by track number")); str.push_back (_("by track name")); + str.push_back (_("by file and track name")); str.push_back (_("by instrument name")); set_popdown_strings (midi_track_name_combo, str); midi_track_name_combo.set_active_text (str.front()); From 4024a4354c3bdd51eb36134983ab73062ffd97c9 Mon Sep 17 00:00:00 2001 From: Ben Loftis Date: Mon, 23 Sep 2024 16:35:04 -0500 Subject: [PATCH 08/75] region grouping: Overdubbing in non-layered mode was assigning wrong groups --- libs/ardour/region.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index eced85fec7..9d6fea03cb 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -126,7 +126,7 @@ Region::get_region_operation_group_id (uint64_t old_region_group, RegionOperatio /* if a region group has not been assigned for this key, assign one */ if (_operation_rgroup_map.find (region_group_key) == _operation_rgroup_map.end ()) { - _operation_rgroup_map[region_group_key] = _next_group_id++; + _operation_rgroup_map[region_group_key] = ++_next_group_id; } return ((_operation_rgroup_map[region_group_key] << 4) | expl); From 68d7c97b0d3de8934af9edd5269b464e6afdff16 Mon Sep 17 00:00:00 2001 From: Alexandre Prokoudine Date: Tue, 24 Sep 2024 15:45:30 +0200 Subject: [PATCH 09/75] Add the missing closing tag, mea maxima culpa --- share/midi_maps/AKAI_MPKmini_mk3.map | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/midi_maps/AKAI_MPKmini_mk3.map b/share/midi_maps/AKAI_MPKmini_mk3.map index cb152382ae..392b1d67c5 100644 --- a/share/midi_maps/AKAI_MPKmini_mk3.map +++ b/share/midi_maps/AKAI_MPKmini_mk3.map @@ -48,3 +48,5 @@ + + From d3f536a7ffe7f099b3ba2b460624f39aa35b83c4 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 24 Sep 2024 00:11:28 +0200 Subject: [PATCH 10/75] Remove unused variable --- libs/audiographer/audiographer/general/cmdpipe_writer.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/audiographer/audiographer/general/cmdpipe_writer.h b/libs/audiographer/audiographer/general/cmdpipe_writer.h index 63ccefb3bd..249ccf8573 100644 --- a/libs/audiographer/audiographer/general/cmdpipe_writer.h +++ b/libs/audiographer/audiographer/general/cmdpipe_writer.h @@ -113,7 +113,6 @@ private: samplecnt_t samples_written; ARDOUR::SystemExec* _proc; std::string _path; - std::vector _tmpfile_path_buf; int _tmp_fd; gchar* _tmp_file; From 72cac07ae7b3b90681dc8c015fdfd96820117e49 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 24 Sep 2024 16:10:07 +0200 Subject: [PATCH 11/75] Generic MIDI: do not send touch events for all mapped controlers Previously a start-touch was sent for any bound MIDI Controllable on each incoming MIDI Control event. --- .../surfaces/generic_midi/midicontrollable.cc | 210 +++++++++--------- 1 file changed, 106 insertions(+), 104 deletions(-) diff --git a/libs/surfaces/generic_midi/midicontrollable.cc b/libs/surfaces/generic_midi/midicontrollable.cc index 136a3cd64a..f7239a3aa3 100644 --- a/libs/surfaces/generic_midi/midicontrollable.cc +++ b/libs/surfaces/generic_midi/midicontrollable.cc @@ -327,6 +327,10 @@ MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool /*is_on*/) void MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg) { + if (control_additional != msg->controller_number) { + return; + } + if (!_controllable) { if (lookup_controllable ()) { return; @@ -337,109 +341,106 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg) _surface->maybe_start_touch (_controllable); - if (control_additional == msg->controller_number) { + if (!_controllable->is_toggle()) { + if (get_encoder() == No_enc) { + float new_value = msg->value; + float max_value = max(last_controllable_value, new_value); + float min_value = min(last_controllable_value, new_value); + float range = max_value - min_value; + float threshold = (float) _surface->threshold (); - if (!_controllable->is_toggle()) { - if (get_encoder() == No_enc) { - float new_value = msg->value; - float max_value = max(last_controllable_value, new_value); - float min_value = min(last_controllable_value, new_value); - float range = max_value - min_value; - float threshold = (float) _surface->threshold (); + bool const in_sync = ( + range < threshold && + _controllable->get_value() <= midi_to_control(max_value) && + _controllable->get_value() >= midi_to_control(min_value) + ); - bool const in_sync = ( - range < threshold && - _controllable->get_value() <= midi_to_control(max_value) && - _controllable->get_value() >= midi_to_control(min_value) - ); - - /* If the surface is not motorised, we try to prevent jumps when - the MIDI controller and controllable are out of sync. - There might be a better way of doing this. - */ - - if (in_sync || _surface->motorised ()) { - _controllable->set_value (midi_to_control (new_value), Controllable::UseGroup); - } - DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI CC %1 value %2 %3\n", (int) msg->controller_number, (float) midi_to_control(new_value), current_uri() )); - - last_controllable_value = new_value; - } else { - uint32_t cur_val = control_to_midi(_controllable->get_value ()); - int offset = (msg->value & 0x3f); - switch (get_encoder()) { - case Enc_L: - if (msg->value & 0x40) { - _controllable->set_value (midi_to_control (cur_val - offset), Controllable::UseGroup); - } else { - _controllable->set_value (midi_to_control (cur_val + offset + 1), Controllable::UseGroup); - } - break; - case Enc_R: - if (msg->value & 0x40) { - _controllable->set_value (midi_to_control (cur_val + offset + 1), Controllable::UseGroup); - } else { - _controllable->set_value (midi_to_control (cur_val - offset), Controllable::UseGroup); - } - break; - case Enc_2: - // 0x40 is max pos offset - if (msg->value > 0x40) { - _controllable->set_value (midi_to_control (cur_val - (0x7f - msg->value)), Controllable::UseGroup); - } else { - _controllable->set_value (midi_to_control (cur_val + msg->value + 1), Controllable::UseGroup); - } - break; - case Enc_B: - if (msg->value > 0x40) { - _controllable->set_value (midi_to_control (cur_val + offset + 1), Controllable::UseGroup); - } else if (msg->value < 0x40) { - _controllable->set_value (midi_to_control (cur_val - (0x40 - msg->value)), Controllable::UseGroup); - } // 0x40 = 0 do nothing - break; - default: - break; - } - DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI CC %1 value %2 %3\n", (int) msg->controller_number, (int) cur_val, current_uri() )); + /* If the surface is not motorised, we try to prevent jumps when + the MIDI controller and controllable are out of sync. + There might be a better way of doing this. + */ + if (in_sync || _surface->motorised ()) { + _controllable->set_value (midi_to_control (new_value), Controllable::UseGroup); } - } else { + DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI CC %1 value %2 %3\n", (int) msg->controller_number, (float) midi_to_control(new_value), current_uri() )); - switch (get_ctltype()) { - case Ctl_Dial: - /* toggle value whenever direction of knob motion changes */ - if (last_incoming > 127) { - /* relax ... first incoming message */ - } else { - if (msg->value > last_incoming) { - _controllable->set_value (1.0, Controllable::UseGroup); + last_controllable_value = new_value; + } else { + uint32_t cur_val = control_to_midi(_controllable->get_value ()); + int offset = (msg->value & 0x3f); + switch (get_encoder()) { + case Enc_L: + if (msg->value & 0x40) { + _controllable->set_value (midi_to_control (cur_val - offset), Controllable::UseGroup); } else { - _controllable->set_value (0.0, Controllable::UseGroup); + _controllable->set_value (midi_to_control (cur_val + offset + 1), Controllable::UseGroup); } - DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("dial Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri())); - } - last_incoming = msg->value; - break; - case Ctl_Momentary: - /* toggle it if over 64, otherwise leave it alone. This behaviour that works with buttons which send a value > 64 each - * time they are pressed. - */ - if (msg->value >= 0x40) { - _controllable->set_value (_controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup); - DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("toggle Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri())); - } - break; - case Ctl_Toggle: - /* toggle if value is over 64, otherwise turn it off. This is behaviour designed for buttons which send a value > 64 when pressed, - maintain state (i.e. they know they were pressed) and then send zero the next time. - */ - if (msg->value >= 0x40) { - _controllable->set_value (_controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup); - } else { - _controllable->set_value (0.0, Controllable::NoGroup); - DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi CC %1 value 0 %2\n", (int) msg->controller_number, current_uri())); break; + case Enc_R: + if (msg->value & 0x40) { + _controllable->set_value (midi_to_control (cur_val + offset + 1), Controllable::UseGroup); + } else { + _controllable->set_value (midi_to_control (cur_val - offset), Controllable::UseGroup); + } + break; + case Enc_2: + // 0x40 is max pos offset + if (msg->value > 0x40) { + _controllable->set_value (midi_to_control (cur_val - (0x7f - msg->value)), Controllable::UseGroup); + } else { + _controllable->set_value (midi_to_control (cur_val + msg->value + 1), Controllable::UseGroup); + } + break; + case Enc_B: + if (msg->value > 0x40) { + _controllable->set_value (midi_to_control (cur_val + offset + 1), Controllable::UseGroup); + } else if (msg->value < 0x40) { + _controllable->set_value (midi_to_control (cur_val - (0x40 - msg->value)), Controllable::UseGroup); + } // 0x40 = 0 do nothing + break; + default: + break; + } + DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI CC %1 value %2 %3\n", (int) msg->controller_number, (int) cur_val, current_uri() )); + + } + } else { + + switch (get_ctltype()) { + case Ctl_Dial: + /* toggle value whenever direction of knob motion changes */ + if (last_incoming > 127) { + /* relax ... first incoming message */ + } else { + if (msg->value > last_incoming) { + _controllable->set_value (1.0, Controllable::UseGroup); + } else { + _controllable->set_value (0.0, Controllable::UseGroup); } + DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("dial Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri())); + } + last_incoming = msg->value; + break; + case Ctl_Momentary: + /* toggle it if over 64, otherwise leave it alone. This behaviour that works with buttons which send a value > 64 each + * time they are pressed. + */ + if (msg->value >= 0x40) { + _controllable->set_value (_controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup); + DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("toggle Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri())); + } + break; + case Ctl_Toggle: + /* toggle if value is over 64, otherwise turn it off. This is behaviour designed for buttons which send a value > 64 when pressed, + maintain state (i.e. they know they were pressed) and then send zero the next time. + */ + if (msg->value >= 0x40) { + _controllable->set_value (_controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup); + } else { + _controllable->set_value (0.0, Controllable::NoGroup); + DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi CC %1 value 0 %2\n", (int) msg->controller_number, current_uri())); + break; } } } @@ -448,6 +449,10 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg) void MIDIControllable::midi_sense_program_change (Parser &, MIDI::byte msg) { + if (msg != control_additional) { + return; + } + if (!_controllable) { if (lookup_controllable ()) { return; @@ -458,16 +463,13 @@ MIDIControllable::midi_sense_program_change (Parser &, MIDI::byte msg) _surface->maybe_start_touch (_controllable); - if (msg == control_additional) { - - if (!_controllable->is_toggle()) { - _controllable->set_value (1.0, Controllable::UseGroup); - DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI program %1 value 1.0 %3\n", (int) msg, current_uri() )); - } else { - float new_value = _controllable->get_value() > 0.5f ? 0.0f : 1.0f; - _controllable->set_value (new_value, Controllable::UseGroup); - DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI program %1 value %2 %3\n", (int) msg, (float) new_value, current_uri())); - } + if (!_controllable->is_toggle()) { + _controllable->set_value (1.0, Controllable::UseGroup); + DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI program %1 value 1.0 %3\n", (int) msg, current_uri() )); + } else { + float new_value = _controllable->get_value() > 0.5f ? 0.0f : 1.0f; + _controllable->set_value (new_value, Controllable::UseGroup); + DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI program %1 value %2 %3\n", (int) msg, (float) new_value, current_uri())); } last_value = (MIDI::byte) (_controllable->get_value() * 127.0); // to prevent feedback fights From 0e28620a7afbbc0f9018c3c7df7e4d95fbc70311 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 24 Sep 2024 14:50:35 -0600 Subject: [PATCH 12/75] prevent Session::StateSaved being emitted when doing save-as with no switch-to The signal would be emitted while the session was temporarily renamed, making it appear that we had switched to the new session, despite instructions not to do so. We had not actually done so, but the saved-as name would show up on window titles. --- libs/ardour/ardour/session.h | 9 +++++---- libs/ardour/session_state.cc | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index ebf15513a2..bfc029ff93 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -1633,10 +1633,11 @@ private: XMLTree* state_tree; StateOfTheState _state_of_the_state; - friend class StateProtector; - std::atomic _suspend_save; - volatile bool _save_queued; - volatile bool _save_queued_pending; + friend class StateProtector; + std::atomic _suspend_save; + volatile bool _save_queued; + volatile bool _save_queued_pending; + bool _no_save_signal; Glib::Threads::Mutex save_state_lock; Glib::Threads::Mutex save_source_lock; diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index c09ff047f3..f6851f9ba4 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -751,7 +751,9 @@ Session::remove_state (string snapshot_name) xml_path, g_strerror (errno)) << endmsg; } - StateSaved (snapshot_name); /* EMIT SIGNAL */ + if (!_no_save_signal) { + StateSaved (snapshot_name); /* EMIT SIGNAL */ + } } /** @param snapshot_name Name to save under, without .ardour / .pending prefix */ @@ -924,7 +926,9 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot unset_dirty (/* EMIT SIGNAL */ true); } - StateSaved (snapshot_name); /* EMIT SIGNAL */ + if (!_no_save_signal) { + StateSaved (snapshot_name); /* EMIT SIGNAL */ + } } #ifndef NDEBUG @@ -5544,11 +5548,18 @@ Session::save_as (SaveAs& saveas) store_recent_sessions (_name, _path); + std::cerr << "Saveas, switch to: " << saveas.switch_to << std::endl; + if (!saveas.switch_to) { + std::cerr << "no switch to!\n"; + /* save the new state */ - save_state ("", false, false, !saveas.include_media); + { + PBD::Unwinder uw (_no_save_signal, true); + save_state ("", false, false, !saveas.include_media); + } /* switch back to the way things were */ From 3d1c414a947dd76ef13cd7a394b2b6639a81d06b Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 24 Sep 2024 14:52:06 -0600 Subject: [PATCH 13/75] initialize member variable --- libs/ardour/session.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 538eedb750..969c2cd88e 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -253,6 +253,7 @@ Session::Session (AudioEngine &eng, , _state_of_the_state (StateOfTheState (CannotSave | InitialConnecting | Loading)) , _save_queued (false) , _save_queued_pending (false) + , _no_save_signal (false) , _last_roll_location (0) , _last_roll_or_reversal_location (0) , _last_record_location (0) From 74bbcdcae7ad6dc5ddf8d99554eaf83d4a35b663 Mon Sep 17 00:00:00 2001 From: Alexandre Prokoudine Date: Wed, 25 Sep 2024 17:11:27 +0200 Subject: [PATCH 14/75] Closing tags should also have proper indentation in XML --- share/midi_maps/AKAI_MPKmini_mk3.map | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/midi_maps/AKAI_MPKmini_mk3.map b/share/midi_maps/AKAI_MPKmini_mk3.map index 392b1d67c5..f59cf4749b 100644 --- a/share/midi_maps/AKAI_MPKmini_mk3.map +++ b/share/midi_maps/AKAI_MPKmini_mk3.map @@ -49,4 +49,4 @@ - + \ No newline at end of file From 5af023c70b4fb41636508e9ed96c12bb7fcf078d Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Wed, 25 Sep 2024 19:34:38 +0200 Subject: [PATCH 15/75] Fix some debug messages --- libs/ardour/disk_reader.cc | 2 +- libs/ardour/session_transport.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc index 57976725cb..bdee3afb65 100644 --- a/libs/ardour/disk_reader.cc +++ b/libs/ardour/disk_reader.cc @@ -508,7 +508,7 @@ midi: } } else { if ((samplecnt_t)c->front ()->rbuf->write_space () >= _chunk_samples) { - DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: write space = %2 of %3\n", name (), c->front ()->rbuf->write_space (), + DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: write space = %2 chunk size = %3\n", name (), c->front ()->rbuf->write_space (), _chunk_samples)); butler_required = true; } diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 1937dbdca5..3398d3b649 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -190,7 +190,7 @@ Session::locate (samplepos_t target_sample, bool for_loop_end, bool force, bool * changes in the value of _transport_sample. */ - DEBUG_TRACE (DEBUG::Transport, string_compose ("rt-locate to %1 ts = %7, for loop end %2 force %3 mmc %4\n", + DEBUG_TRACE (DEBUG::Transport, string_compose ("rt-locate to %1 ts = %5, for loop end %2 force %3 mmc %4\n", target_sample, for_loop_end, force, with_mmc, _transport_sample)); if (!force && (_transport_sample == target_sample) && !for_loop_end) { From 18e0cba1cbe2deb81b88c904cfb40362a77abe78 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Wed, 25 Sep 2024 20:03:48 +0200 Subject: [PATCH 16/75] Fix underruns when looping/locating and changing region content Previously loop-wrap around asked the butler to perform a unnecessary seek operation next time it is summoned. If the butler is then summoned for a PostTransportOverWrite event, the seek causes a DR::Underrun. --- libs/ardour/disk_reader.cc | 6 ++++-- libs/ardour/session_transport.cc | 14 ++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc index bdee3afb65..09351523d3 100644 --- a/libs/ardour/disk_reader.cc +++ b/libs/ardour/disk_reader.cc @@ -560,6 +560,7 @@ DiskReader::pending_overwrite () const void DiskReader::set_pending_overwrite (OverwriteReason why) { + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1 set_pending_overwrite because %3%4%5\n", owner ()->name (), std::hex, why, std::dec)); std::shared_ptr c = channels.reader (); /* called from audio thread, so we can use the read ptr and playback sample as we wish */ @@ -862,9 +863,10 @@ DiskReader::seek (samplepos_t sample, bool complete_refill) } } - _pending_overwrite.store (OverwriteReason (0)); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("DiskReader::seek %1 %2 -> %3 refill=%4 pending_overwrite = %5\n", + owner ()->name (), playback_sample, sample, complete_refill, _pending_overwrite.load ())); - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("DiskReader::seek %1 %2 -> %3 refill=%4\n", owner ()->name ().c_str (), playback_sample, sample, complete_refill)); + _pending_overwrite.store (OverwriteReason (0)); const samplecnt_t distance = sample - playback_sample; if (!complete_refill && can_internal_playback_seek (distance)) { diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 3398d3b649..a6472b46da 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -202,9 +202,12 @@ Session::locate (samplepos_t target_sample, bool for_loop_end, bool force, bool // Update Timecode time _transport_sample = target_sample; _nominal_jack_transport_sample = boost::none; - // Bump seek counter so that any in-process locate in the butler - // thread(s?) can restart. - _seek_counter.fetch_add (1); + + /* Note that loop wrap-around locates do not need to call "seek" */ + if (force || !for_loop_end) { + /* Bump seek counter so that any in-process locate in the butler can restart */ + _seek_counter.fetch_add (1); + } _last_roll_or_reversal_location = target_sample; if (!for_loop_end && !_exporting) { _remaining_latency_preroll = worst_latency_preroll_buffer_size_ceil (); @@ -1206,7 +1209,10 @@ Session::butler_transport_work (bool have_process_lock) non_realtime_locate (); } - if (ptw & PostTransportOverWrite) { + /* if we just performed a locate, buffers have been refilled. + * This effectively has done the work of "PostTransportOverWrite" already. + */ + else if (ptw & PostTransportOverWrite) { non_realtime_overwrite (on_entry, finished, (ptw & PostTransportLoopChanged)); if (!finished) { (void) PBD::atomic_dec_and_test (_butler->should_do_transport_work); From 60b3b24cc8242f12534464ccfb8305fcb2cb813e Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 24 Sep 2024 18:42:58 +0200 Subject: [PATCH 17/75] Do not allow changing the loop range while recording --- gtk2_ardour/editor.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 6a55099cf8..54fb70bd76 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -5304,7 +5304,12 @@ Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_ void Editor::set_loop_range (timepos_t const & start, timepos_t const & end, string cmd) { - if (!_session) return; + if (!_session) { + return; + } + if (_session->get_play_loop () && _session->actively_recording ()) { + return; + } begin_reversible_command (cmd); From 0357c12fccb854ecc7c7d25b0b7630a22b29cbe3 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 26 Sep 2024 01:25:54 +0200 Subject: [PATCH 18/75] Really fix compiler warnings for clang < v15 see also 57a67738335c8d055599c5792d014bc6d7c6bc21 --- wscript | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wscript b/wscript index ff547a80d1..bf434811b2 100644 --- a/wscript +++ b/wscript @@ -165,8 +165,7 @@ compiler_flags_dictionaries['clang-darwin'] = clang_darwin_dict # Xcode 15 does not like our boost version, producing warnings from almost every file clang15_darwin_dict = compiler_flags_dictionaries['clang-darwin'].copy() -clang15_darwin_dict['basic-warnings'].append ("-Wno-deprecated-builtins") -clang15_darwin_dict['basic-warnings'].append ("-Wno-deprecated-copy-with-user-provided-copy") +clang15_darwin_dict['basic-warnings'] = clang15_darwin_dict['basic-warnings'] + ["-Wno-deprecated-builtins", "-Wno-deprecated-copy-with-user-provided-copy"] compiler_flags_dictionaries['clang15-darwin'] = clang15_darwin_dict # Version stuff From 5d65d7f0516bedbb896396678c648f57e6e081e6 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Wed, 25 Sep 2024 04:26:22 +0200 Subject: [PATCH 19/75] Peakdata: correctly set peaks of left and right end --- libs/ardour/audiosource.cc | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index 7988b75fd6..1f712bf7bc 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -583,9 +583,10 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos /* compute the rounded up sample position */ - samplepos_t current_stored_peak = (samplepos_t) ceil (start / (double) samples_per_file_peak); - samplepos_t next_visual_peak = (samplepos_t) ceil (start / samples_per_visual_peak); - double next_visual_peak_sample = next_visual_peak * samples_per_visual_peak; + samplepos_t current_stored_peak = (samplepos_t) ceil (start / (double) samples_per_file_peak); + samplepos_t next_visual_peak = (samplepos_t) ceil (start / samples_per_visual_peak); + double next_visual_peak_sample = next_visual_peak * samples_per_visual_peak; + samplepos_t stored_peak_before_next_visual_peak = (samplepos_t) next_visual_peak_sample / samples_per_file_peak; samplecnt_t nvisual_peaks = 0; uint32_t i = 0; @@ -603,6 +604,10 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos size_t map_length = (chunksize * sizeof(PeakData)) + map_delta; if (_first_run || (_last_scale != samples_per_visual_peak) || (_last_map_off != map_off) || (_last_raw_map_length < raw_map_length)) { + + /* offset between requested start, and first peak-file peak */ + samplecnt_t start_offset = next_visual_peak_sample - start; + peak_cache.reset (new PeakData[npeaks]); boost::scoped_array staging (new PeakData[chunksize]); @@ -665,6 +670,22 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_sample / samples_per_file_peak; } + /* add data between start and sample corresponding to map_off */ + if (start_offset > 0) { + boost::scoped_array buf(new Sample[start_offset]); + samplecnt_t samples_read = read_unlocked (buf.get(), start, start_offset); + find_peaks (buf.get(), samples_read, &peak_cache[0].min, &peak_cache[0].max); + } + + /* fix end, add data not covered by Peak File */ + samplecnt_t last_sample_from_peakfile = current_stored_peak * samples_per_file_peak; + if (last_sample_from_peakfile < start + cnt && nvisual_peaks > 0) { + samplecnt_t to_read = start + cnt - last_sample_from_peakfile; + boost::scoped_array buf(new Sample[to_read]); + samplecnt_t samples_read = read_unlocked (buf.get(), last_sample_from_peakfile, to_read); + find_peaks (buf.get(), samples_read, &peak_cache[nvisual_peaks - 1].min, &peak_cache[nvisual_peaks - 1].max); + } + if (zero_fill) { #ifndef NDEBUG cerr << "Zero fill '" << _name << "' end of peaks (@ " << read_npeaks << " with " << zero_fill << ")" << endl; From b28090c64c8efec4f8503bd984e9d87f1f7faa65 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Wed, 25 Sep 2024 04:36:13 +0200 Subject: [PATCH 20/75] Peakdata: start striding at consistent offsets --- libs/ardour/audiosource.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index 1f712bf7bc..6d45a56f2f 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -583,9 +583,9 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos /* compute the rounded up sample position */ - samplepos_t current_stored_peak = (samplepos_t) ceil (start / (double) samples_per_file_peak); samplepos_t next_visual_peak = (samplepos_t) ceil (start / samples_per_visual_peak); double next_visual_peak_sample = next_visual_peak * samples_per_visual_peak; + samplepos_t current_stored_peak = (samplepos_t) ceil (next_visual_peak_sample / (double) samples_per_file_peak); samplepos_t stored_peak_before_next_visual_peak = (samplepos_t) next_visual_peak_sample / samples_per_file_peak; samplecnt_t nvisual_peaks = 0; @@ -593,11 +593,11 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos /* handle the case where the initial visual peak is on a pixel boundary */ - current_stored_peak = min (current_stored_peak, stored_peak_before_next_visual_peak); + //current_stored_peak = min (current_stored_peak, stored_peak_before_next_visual_peak); /* open ... close during out: handling */ - off_t map_off = (uint32_t) (ceil (start / (double) samples_per_file_peak)) * sizeof(PeakData); + off_t map_off = (uint32_t) (current_stored_peak) * sizeof(PeakData); off_t read_map_off = map_off & ~(bufsize - 1); off_t map_delta = map_off - read_map_off; size_t raw_map_length = chunksize * sizeof(PeakData); From 68eb63e0c8b54189786b11caaf5ebe1d777816c8 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 26 Sep 2024 01:09:07 +0200 Subject: [PATCH 21/75] Waveview: fix rounding, span entire region rectangle Previously samples at the end could end up being cropped, when the Waveview is rendered using an offset. --- libs/waveview/wave_view.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libs/waveview/wave_view.cc b/libs/waveview/wave_view.cc index fcb181dfb2..7d406306ae 100644 --- a/libs/waveview/wave_view.cc +++ b/libs/waveview/wave_view.cc @@ -307,7 +307,7 @@ WaveView::get_item_and_draw_rect_in_window_coords (Rect const& canvas_rect, Rect */ double const width = region_length() / _props->samples_per_pixel; - item_rect = item_to_window (Rect (0.0, 0.0, width, _props->height)); + item_rect = item_to_window (Rect (0.0, 0.0, width, _props->height), false); /* Now lets get the intersection with the area we've been asked to draw */ @@ -318,6 +318,11 @@ WaveView::get_item_and_draw_rect_in_window_coords (Rect const& canvas_rect, Rect return false; } + item_rect.x0 = floor (item_rect.x0); + item_rect.x1 = ceil (item_rect.x1); + item_rect.y0 = round (item_rect.y0); + item_rect.y1 = round (item_rect.y1); + /* draw_rect now defines the rectangle we need to update/render the waveview * into, in window coordinate space. * @@ -325,7 +330,7 @@ WaveView::get_item_and_draw_rect_in_window_coords (Rect const& canvas_rect, Rect * and/or end. */ draw_rect.x0 = floor (draw_rect.x0); - draw_rect.x1 = floor (draw_rect.x1); + draw_rect.x1 = ceil (draw_rect.x1); return true; } From 9d0415ba04008b5f82516121d1009b7587a00c7e Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 26 Sep 2024 02:20:35 +0200 Subject: [PATCH 22/75] Waveview: fix visual jitter when trimming or splitting regions The left edge of a region on the the editor is rounded to be on a pixel. When zoomed out, the position corresponding to that pixel is not usually identical to the region's position(). We need to correct for this as best as possible, while keeping peaks aligned to pixels. --- libs/waveview/wave_view.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/waveview/wave_view.cc b/libs/waveview/wave_view.cc index 7d406306ae..79365b1432 100644 --- a/libs/waveview/wave_view.cc +++ b/libs/waveview/wave_view.cc @@ -1091,12 +1091,19 @@ WaveView::render (Rect const & area, Cairo::RefPtr context) cons assert (image_to_draw); + /* Calculate the sample that corresponds to the region-rectangle's left edge + * in the editor at current zoom (see TimeAxisViewItem::set_position). + */ + double const samples_per_pixel = _props->samples_per_pixel; + samplepos_t const region_position = _region->position().samples(); + samplepos_t const region_view_x = round (round (region_position / samples_per_pixel) * samples_per_pixel); + ARDOUR::sampleoffset_t region_view_dx = region_position - region_view_x; + /* compute the first pixel of the image that should be used when we * render the specified range. */ - double image_origin_in_self_coordinates = - (image_to_draw->props.get_sample_start () - _props->region_start) / _props->samples_per_pixel; + double image_origin_in_self_coordinates = (image_to_draw->props.get_sample_start () - _props->region_start + region_view_dx) / samples_per_pixel; /* the image may only be a best-effort ... it may not span the entire * range requested, though it is guaranteed to cover the start. So From 75e6a77c3e1f3ae3773d3433b5fb523af7b7fc49 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 26 Sep 2024 02:26:30 +0200 Subject: [PATCH 23/75] Fix another debug message --- libs/ardour/disk_reader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc index 09351523d3..05255066fe 100644 --- a/libs/ardour/disk_reader.cc +++ b/libs/ardour/disk_reader.cc @@ -560,7 +560,7 @@ DiskReader::pending_overwrite () const void DiskReader::set_pending_overwrite (OverwriteReason why) { - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1 set_pending_overwrite because %3%4%5\n", owner ()->name (), std::hex, why, std::dec)); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1 set_pending_overwrite because %2%3%4\n", owner ()->name (), std::hex, why, std::dec)); std::shared_ptr c = channels.reader (); /* called from audio thread, so we can use the read ptr and playback sample as we wish */ From e1f465a952a17719fcd050c708c10cfc16e3f8a8 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 26 Sep 2024 03:14:18 +0200 Subject: [PATCH 24/75] NO-OP: improve butler and disk-i/o debug messages --- libs/ardour/butler.cc | 4 ++-- libs/ardour/disk_reader.cc | 34 ++++++++++++++++++-------------- libs/ardour/session_transport.cc | 9 ++++----- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/libs/ardour/butler.cc b/libs/ardour/butler.cc index 7fb6c6ee39..2a31403a1f 100644 --- a/libs/ardour/butler.cc +++ b/libs/ardour/butler.cc @@ -273,7 +273,7 @@ Butler::thread_work () RouteList rl_with_auditioner = *rl; rl_with_auditioner.push_back (_session.the_auditioner ()); - 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", should_do_transport_work.load ())); std::shared_ptr tl = _session.io_tasklist (); @@ -413,8 +413,8 @@ Butler::flush_tracks_to_disk_normal (std::shared_ptr rl, uint32 void Butler::schedule_transport_work () { - DEBUG_TRACE (DEBUG::Butler, "requesting more transport work\n"); should_do_transport_work.fetch_add (1); + DEBUG_TRACE (DEBUG::Butler, string_compose ("requesting more transport work (now %1)\n", should_do_transport_work.load ())); summon (); } diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc index 05255066fe..5210b13028 100644 --- a/libs/ardour/disk_reader.cc +++ b/libs/ardour/disk_reader.cc @@ -115,7 +115,7 @@ DiskReader::add_channel_to (std::shared_ptr c, uint32_t how_many) { while (how_many--) { c->push_back (new ReaderChannelInfo (_session.butler ()->audio_playback_buffer_size (), loop_fade_length)); - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: new reader channel, write space = %2 read = %3\n", + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': new reader channel, write space = %2 read = %3\n", name (), c->back ()->rbuf->write_space (), c->back ()->rbuf->read_space ())); @@ -422,7 +422,7 @@ DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp #ifndef NDEBUG // not rt-safe to print here cerr << "underrun for " << _name << " Available samples: " << available << " required: " << disk_samples_to_consume << endl; #endif - DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, total space = %3 vs %4\n", DEBUG_THREAD_SELF, name (), available, disk_samples_to_consume)); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': underrun in thread %2, available = %3 need = %4\n", owner ()->name (), DEBUG_THREAD_SELF, available, disk_samples_to_consume)); DEBUG_TRACE (DEBUG::AudioCacheRefill, string_compose ("DR '%1' underrun have %2 need %3 samples at pos %4\n", name (), available, disk_samples_to_consume, std::setprecision (3), std::fixed, @@ -503,13 +503,14 @@ midi: if (!c->empty ()) { if (_slaved) { if (c->front ()->rbuf->write_space () >= c->front ()->rbuf->bufsize () / 2) { - DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: slaved, write space = %2 of %3\n", name (), c->front ()->rbuf->write_space (), c->front ()->rbuf->bufsize ())); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': slaved, write space = %2 of %3\n", + _owner->name (), c->front ()->rbuf->write_space (), c->front ()->rbuf->bufsize ())); butler_required = true; } } else { if ((samplecnt_t)c->front ()->rbuf->write_space () >= _chunk_samples) { - DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: write space = %2 chunk size = %3\n", name (), c->front ()->rbuf->write_space (), - _chunk_samples)); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: write space = %2 chunk size = %3\n", + _owner->name (), c->front ()->rbuf->write_space (), _chunk_samples)); butler_required = true; } } @@ -524,7 +525,7 @@ midi: } if (_need_butler) { - DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 reader run, needs butler = %2\n", name (), _need_butler)); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1' reader run, needs butler = %2\n", _owner->name (), _need_butler)); } } @@ -560,7 +561,7 @@ DiskReader::pending_overwrite () const void DiskReader::set_pending_overwrite (OverwriteReason why) { - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1 set_pending_overwrite because %2%3%4\n", owner ()->name (), std::hex, why, std::dec)); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': set_pending_overwrite because %2%3%4)\n", owner ()->name (), std::hex, why, std::dec)); std::shared_ptr c = channels.reader (); /* called from audio thread, so we can use the read ptr and playback sample as we wish */ @@ -699,6 +700,8 @@ DiskReader::overwrite_existing_audio () const size_t to_overwrite = c->front ()->rbuf->overwritable_at (overwrite_offset); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': overwrite_existing_audio at %2 offset = %2 to_ovrwrt = %3\n", owner ()->name (), overwrite_sample, overwrite_offset, to_overwrite)); + chunk1_offset = overwrite_offset; chunk1_cnt = min (c->front ()->rbuf->bufsize () - (size_t)overwrite_offset, to_overwrite); @@ -763,7 +766,7 @@ DiskReader::overwrite_existing_audio () } if (!rci->initialized) { - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("Init ReaderChannel '%1' overwriting at: %2, avail: %3\n", name (), overwrite_sample, chan->rbuf->read_space ())); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'1': Init ReaderChannel overwriting at: %2, avail: %3\n", name (), overwrite_sample, chan->rbuf->read_space ())); if (chan->rbuf->read_space () > 0) { rci->initialized = true; } @@ -807,7 +810,7 @@ DiskReader::overwrite_existing_buffers () { /* called from butler thread */ - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1 overwriting existing buffers at %2 (because %3%4%5\n", owner ()->name (), overwrite_sample, std::hex, _pending_overwrite.load (), std::dec)); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': overwriting existing buffers at %2 (because %3%4%5)\n", owner ()->name (), overwrite_sample, std::hex, _pending_overwrite.load (), std::dec)); bool ret = true; @@ -823,6 +826,7 @@ DiskReader::overwrite_existing_buffers () } } + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': Clear _pending_overwrite\n", owner ()->name ())); _pending_overwrite.store (OverwriteReason (0)); return ret; @@ -863,7 +867,7 @@ DiskReader::seek (samplepos_t sample, bool complete_refill) } } - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("DiskReader::seek %1 %2 -> %3 refill=%4 pending_overwrite = %5\n", + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': seek %2 -> %3 refill = %4 pending_overwrite = %5\n", owner ()->name (), playback_sample, sample, complete_refill, _pending_overwrite.load ())); _pending_overwrite.store (OverwriteReason (0)); @@ -1194,7 +1198,7 @@ DiskReader::refill_audio (Sample* sum_buffer, Sample* mixdown_buffer, float* gai samplecnt_t total_space = c->front ()->rbuf->write_space (); if (total_space == 0) { - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: no space to refill\n", name ())); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': no space to refill\n", name ())); /* nowhere to write to */ return 0; } @@ -1219,7 +1223,7 @@ DiskReader::refill_audio (Sample* sum_buffer, Sample* mixdown_buffer, float* gai * the playback buffer is empty. */ - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: space to refill %2 vs. chunk %3 (speed = %4)\n", name (), total_space, _chunk_samples, _session.transport_speed ())); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': space to refill %2 vs. chunk %3 (speed = %4)\n", name (), total_space, _chunk_samples, _session.transport_speed ())); if ((total_space < _chunk_samples) && fabs (_session.transport_speed ()) < 2.0f) { return 0; } @@ -1230,7 +1234,7 @@ DiskReader::refill_audio (Sample* sum_buffer, Sample* mixdown_buffer, float* gai */ if (_slaved && total_space < (samplecnt_t) (c->front ()->rbuf->bufsize () / 2)) { - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: not enough to refill while slaved\n", this)); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': not enough to refill while slaved\n", this)); return 0; } @@ -1288,7 +1292,7 @@ DiskReader::refill_audio (Sample* sum_buffer, Sample* mixdown_buffer, float* gai /* now back to samples */ samplecnt_t samples_to_read = byte_size_for_read / (bits_per_sample / 8); - DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: will refill %2 channels with %3 samples\n", name (), c->size (), total_space)); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': will refill %2 channels with %3 samples\n", name (), c->size (), total_space)); samplepos_t file_sample_tmp = fsa; @@ -1334,7 +1338,7 @@ DiskReader::refill_audio (Sample* sum_buffer, Sample* mixdown_buffer, float* gai } } if (!rci->initialized) { - DEBUG_TRACE (DEBUG::DiskIO, string_compose (" -- Init ReaderChannel '%1' read: %2 samples, at: %4, avail: %5\n", name (), to_read, file_sample_tmp, rci->rbuf->read_space ())); + DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1' Init ReaderChannel read: %2 samples, at: %4, avail: %5\n", name (), to_read, file_sample_tmp, rci->rbuf->read_space ())); rci->initialized = true; } } diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index a6472b46da..47e92791cb 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -674,7 +674,7 @@ Session::butler_completed_transport_work () ENSURE_PROCESS_THREAD; PostTransportWork ptw = post_transport_work (); - DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler done, RT cleanup for %1\n", enum_2_string (ptw))); + DEBUG_TRACE (DEBUG::Butler, string_compose ("Butler done, RT cleanup for %1\n", enum_2_string (ptw))); if (ptw & PostTransportAudition) { if (auditioner && auditioner->auditioning()) { @@ -1149,7 +1149,7 @@ Session::butler_transport_work (bool have_process_lock) uint64_t before = g_get_monotonic_time(); #endif - DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = [%1] (0x%3%4%5) at %2\n", enum_2_string (ptw), before, std::hex, ptw, std::dec)); + DEBUG_TRACE (DEBUG::Butler, string_compose ("Butler transport work, todo = [%1] (0x%3%4%5) twr = %6 @ %2\n", enum_2_string (ptw), before, std::hex, ptw, std::dec, on_entry)); if (ptw & PostTransportAdjustPlaybackBuffering) { /* need to prevent concurrency with ARDOUR::Reader::run(), @@ -1203,9 +1203,8 @@ Session::butler_transport_work (bool have_process_lock) } } - if (will_locate) { - DEBUG_TRACE (DEBUG::Transport, string_compose ("nonrealtime locate invoked from BTW (butler has done %1, rtlocs %2)\n", butler, rtlocates)); + DEBUG_TRACE (DEBUG::Butler, string_compose ("nonrealtime locate invoked from BTW (butler has done %1, rtlocs %2)\n", butler, rtlocates)); non_realtime_locate (); } @@ -1226,7 +1225,7 @@ Session::butler_transport_work (bool have_process_lock) (void) PBD::atomic_dec_and_test (_butler->should_do_transport_work); - DEBUG_TRACE (DEBUG::Transport, string_compose (X_("Butler transport work all done after %1 usecs @ %2 ptw %3 trw = %4\n"), g_get_monotonic_time() - before, _transport_sample, enum_2_string (post_transport_work()), _butler->transport_work_requested())); + DEBUG_TRACE (DEBUG::Butler, string_compose (X_("Butler transport work all done after %1 usecs tsp = %2 ptw [%3] trw = %4\n"), g_get_monotonic_time() - before, _transport_sample, enum_2_string (post_transport_work()), _butler->should_do_transport_work.load ())); } void From 4f33105ae08793ea2dc44d007cfe3b7977214ed5 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 26 Sep 2024 03:17:00 +0200 Subject: [PATCH 25/75] Process PostTransportOverWrite after all This is needed to properly decrement "should_do_transport_work". After a seek the DR:_pending_overwrite flag will have been cleared, so not much extra work happens here. --- libs/ardour/session_transport.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 47e92791cb..de2989c98f 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1208,10 +1208,7 @@ Session::butler_transport_work (bool have_process_lock) non_realtime_locate (); } - /* if we just performed a locate, buffers have been refilled. - * This effectively has done the work of "PostTransportOverWrite" already. - */ - else if (ptw & PostTransportOverWrite) { + if (ptw & PostTransportOverWrite) { non_realtime_overwrite (on_entry, finished, (ptw & PostTransportLoopChanged)); if (!finished) { (void) PBD::atomic_dec_and_test (_butler->should_do_transport_work); From 180e90b81a839d72efca26993361cf4900a7cd9b Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 26 Sep 2024 03:44:09 +0200 Subject: [PATCH 26/75] Only call non_realtime_locate() when locating `butler_transport_work()` may be called for various reasons, notably PostTransportOverWrite. At that point in time the transport may still be rolling or stopping with rtloc already set. This can cause DR::Underruns since seek clear the entire disk reader buffer. --- libs/ardour/session_transport.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index de2989c98f..c751e3258b 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1203,9 +1203,11 @@ Session::butler_transport_work (bool have_process_lock) } } - if (will_locate) { + if (will_locate && transport_locating ()) { DEBUG_TRACE (DEBUG::Butler, string_compose ("nonrealtime locate invoked from BTW (butler has done %1, rtlocs %2)\n", butler, rtlocates)); non_realtime_locate (); + } else if (will_locate) { + DEBUG_TRACE (DEBUG::Butler, string_compose ("skip nonrealtime locate (butler has done %1, rtlocs %2) ts = %3\n", butler, rtlocates, _transport_fsm->current_state())); } if (ptw & PostTransportOverWrite) { From dc92ed1bc9b9ec965b22235b1f0a717ac081bd92 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 26 Sep 2024 04:39:19 +0200 Subject: [PATCH 27/75] Fix Pre-Fader RegionFx when looping --- libs/ardour/audioregion.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 90a68f0e0f..e813642d41 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -1021,11 +1021,14 @@ AudioRegion::read_at (Sample* buf, mix_buffers_no_gain (buf + fade_in_limit, mixdown_buffer + fade_in_limit, N); } } + samplecnt_t T = _cache_tail; if (T > 0) { T = min (T, can_read); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region %1 adding FX tail of %2 cut to_read %3 at %4 total len = %5 cnt was %6\n", - name (), _cache_tail, T, to_read, to_read + T, cnt)); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region %1 adding FX tail of %2 cut to_read %3 at %4 total len = %5 cnt was %6\n", + name (), _cache_tail, T, to_read, to_read + T, cnt)); + /* limit (to_read + T) == cnt, some cases (e.g. loop) will ignore tail data */ + T = min (T, cnt - to_read); /* AudioPlaylist::read reads regions in reverse order, so we can add the tail here */ mix_buffers_no_gain (buf + to_read, mixdown_buffer + to_read, T); } From 6cc54aa1b089dfc27b7028e80d5715a1ac7b0753 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 26 Sep 2024 16:25:39 +0200 Subject: [PATCH 28/75] Fix playhead moving beyond loop range When some other transport related session event (anything that requires non_realtime_work_pending, e.g. PostTransportOverWrite) coincides with SessionEvent::AutoLoop, the auto-loop event was removed from the main event queue and executed as one-shot immediate event. While looping the AutoLoop event needs to remain on the main event queue. --- libs/ardour/session_process.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index 9d2116192e..e3003127a1 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -911,7 +911,8 @@ Session::process_event (SessionEvent* ev) /* except locates, which we have the capability to handle */ - if (ev->type != SessionEvent::Locate) { + if (ev->type != SessionEvent::Locate && ev->type != SessionEvent::AutoLoop) { + DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("Postponing and moving event to immediate queue: %1 @ %2\n", enum_2_string (ev->type), _transport_sample)); immediate_events.insert (immediate_events.end(), ev); _remove_event (ev); return; From 9945d7721fc2a3d172dc319f000e87dacfdb6d14 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 27 Sep 2024 01:37:14 +0200 Subject: [PATCH 29/75] Allow to investigate I/O thread policy see https://discourse.ardour.org/t/ardour-8-7-unable-to-connect-to-audio-backend/110774/10 https://discourse.ardour.org/t/ardour-8-7-x-run-issues/110767/2 --- libs/ardour/io_tasklist.cc | 39 ++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/libs/ardour/io_tasklist.cc b/libs/ardour/io_tasklist.cc index 7974c8ce85..5e815c5faa 100644 --- a/libs/ardour/io_tasklist.cc +++ b/libs/ardour/io_tasklist.cc @@ -52,19 +52,38 @@ IOTaskList::IOTaskList (uint32_t n_threads) 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)); + bool use_sched_param = true; + int policy = SCHED_RR; + + const char* p = getenv ("ARDOUR_IO_SCHED"); + if (p) { + int pi = atoi (p); + if (pi < 0) { + policy = SCHED_RR; + } else if (pi > 0) { + policy = SCHED_FIFO; + } else { + use_sched_param = false; + } + } + + if (use_sched_param) { + parm.sched_priority = pbd_absolute_rt_priority (SCHED_RR, pbd_pthread_priority (THREAD_IO)); +#ifdef PLATFORM_WINDOWS + pthread_attr_setschedpolicy (&attr, SCHED_OTHER); +#else + pthread_attr_setschedpolicy (&attr, policy); +#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, policy = %3\n", _n_threads, parm.sched_priority, policy)); + } else { + DEBUG_TRACE (PBD::DEBUG::IOTaskList, string_compose ("IOTaskList starting %1 threads with default priority.\n", _n_threads)); + } _workers.resize (_n_threads); for (uint32_t i = 0; i < _n_threads; ++i) { From d8e4e7b259a33e46db5a750c76c691f2d0e7c931 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 27 Sep 2024 02:54:35 +0200 Subject: [PATCH 30/75] Fix calculating intersection of waveform rectangle see also 68eb63e0c8b --- libs/waveview/wave_view.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libs/waveview/wave_view.cc b/libs/waveview/wave_view.cc index 79365b1432..af619a8de5 100644 --- a/libs/waveview/wave_view.cc +++ b/libs/waveview/wave_view.cc @@ -309,6 +309,11 @@ WaveView::get_item_and_draw_rect_in_window_coords (Rect const& canvas_rect, Rect double const width = region_length() / _props->samples_per_pixel; item_rect = item_to_window (Rect (0.0, 0.0, width, _props->height), false); + item_rect.x0 = floor (item_rect.x0); + item_rect.x1 = ceil (item_rect.x1); + item_rect.y0 = round (item_rect.y0); + item_rect.y1 = round (item_rect.y1); + /* Now lets get the intersection with the area we've been asked to draw */ draw_rect = item_rect.intersection (canvas_rect); @@ -317,12 +322,6 @@ WaveView::get_item_and_draw_rect_in_window_coords (Rect const& canvas_rect, Rect // No intersection with drawing area return false; } - - item_rect.x0 = floor (item_rect.x0); - item_rect.x1 = ceil (item_rect.x1); - item_rect.y0 = round (item_rect.y0); - item_rect.y1 = round (item_rect.y1); - /* draw_rect now defines the rectangle we need to update/render the waveview * into, in window coordinate space. * From 3751d20ce99b99addad27b7bf8e1e2071759ea5b Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 27 Sep 2024 06:00:15 +0200 Subject: [PATCH 31/75] Use dedicated thread to start export timespans #9798 This addresses an issue with creating a one-shot thread directly from the realtime callback, which can be problematic on Windows. --- libs/ardour/ardour/export_handler.h | 8 ++- libs/ardour/export_handler.cc | 77 ++++++++++++++++++++++------- 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/libs/ardour/ardour/export_handler.h b/libs/ardour/ardour/export_handler.h index d26d847c91..71c1872632 100644 --- a/libs/ardour/ardour/export_handler.h +++ b/libs/ardour/ardour/export_handler.h @@ -146,7 +146,13 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory, public sigc::tr /* Timespan management */ - static void* start_timespan_bg (void*); + void timespan_thread_wakeup (); + + static void* _timespan_thread_run (void*); + pthread_t _timespan_thread; + std::atomic _timespan_thread_active; + pthread_mutex_t _timespan_mutex; + pthread_cond_t _timespan_cond; int start_timespan (); int process_timespan (samplecnt_t samples); diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc index 46fbc9564e..460ebf3a52 100644 --- a/libs/ardour/export_handler.cc +++ b/libs/ardour/export_handler.cc @@ -25,6 +25,7 @@ #include "pbd/gstdio_compat.h" #include #include +#include #include "pbd/convert.h" @@ -121,6 +122,12 @@ ExportHandler::ExportHandler (Session & session) , cue_tracknum (0) , cue_indexnum (0) { + pthread_mutex_init (&_timespan_mutex, 0); + pthread_cond_init (&_timespan_cond, 0); + _timespan_thread_active.store (1); + if (pthread_create (&_timespan_thread, NULL, _timespan_thread_run, this)) { + _timespan_thread_active.store (0); + } } ExportHandler::~ExportHandler () @@ -130,6 +137,56 @@ ExportHandler::~ExportHandler () session.surround_master ()->surround_return ()->finalize_export (); } graph_builder->cleanup (export_status->aborted () ); + + pthread_mutex_lock (&_timespan_mutex); + _timespan_thread_active.store (0); + pthread_cond_signal (&_timespan_cond); + pthread_mutex_unlock (&_timespan_mutex); + + void *status; + pthread_join (_timespan_thread, &status); + + pthread_cond_destroy (&_timespan_cond); + pthread_mutex_destroy (&_timespan_mutex); +} + +void* +ExportHandler::_timespan_thread_run (void* me) +{ + char name[64]; + snprintf (name, 64, "Export-TS-%p", (void*)DEBUG_THREAD_SELF); + pthread_set_name (name); + ExportHandler* self = static_cast (me); + + SessionEvent::create_per_thread_pool (name, 512); + PBD::notify_event_loops_about_thread_creation (pthread_self(), name, 512); + + pthread_mutex_lock (&self->_timespan_mutex); + while (self->_timespan_thread_active.load ()) { + pthread_cond_wait (&self->_timespan_cond, &self->_timespan_mutex); + if (!self->_timespan_thread_active.load ()) { + break; + } else { + Temporal::TempoMap::fetch (); + self->process_connection.disconnect (); + Glib::Threads::Mutex::Lock l (self->export_status->lock()); + DiskReader::allocate_working_buffers (); + self->start_timespan (); + DiskReader::free_working_buffers (); + } + } + pthread_mutex_unlock (&self->_timespan_mutex); + pthread_exit (0); + return 0; +} + +void +ExportHandler::timespan_thread_wakeup () +{ + if (pthread_mutex_trylock (&_timespan_mutex) == 0) { + pthread_cond_signal (&_timespan_cond); + pthread_mutex_unlock (&_timespan_mutex); + } } /** Add an export to the `to-do' list */ @@ -372,22 +429,6 @@ ExportHandler::command_output(std::string output, size_t size) info << output << endmsg; } -void* -ExportHandler::start_timespan_bg (void* eh) -{ - char name[64]; - snprintf (name, 64, "Export-TS-%p", (void*)DEBUG_THREAD_SELF); - pthread_set_name (name); - ExportHandler* self = static_cast (eh); - self->process_connection.disconnect (); - Glib::Threads::Mutex::Lock l (self->export_status->lock()); - SessionEvent::create_per_thread_pool (name, 512); - DiskReader::allocate_working_buffers (); - self->start_timespan (); - DiskReader::free_working_buffers (); - return 0; -} - void ExportHandler::finish_timespan () { @@ -543,9 +584,7 @@ ExportHandler::finish_timespan () /* finish timespan is called in freewheeling rt-context, * we cannot start a new export from here */ assert (AudioEngine::instance()->freewheeling ()); - pthread_t tid; - pthread_create (&tid, NULL, ExportHandler::start_timespan_bg, this); - pthread_detach (tid); + timespan_thread_wakeup (); } void From 71a3161252ecf25530c8575b5489034b9f63ffae Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 27 Sep 2024 17:20:42 +0200 Subject: [PATCH 32/75] Error handling for 3751d20ce --- libs/ardour/export_handler.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc index 460ebf3a52..fe566a0108 100644 --- a/libs/ardour/export_handler.cc +++ b/libs/ardour/export_handler.cc @@ -127,6 +127,8 @@ ExportHandler::ExportHandler (Session & session) _timespan_thread_active.store (1); if (pthread_create (&_timespan_thread, NULL, _timespan_thread_run, this)) { _timespan_thread_active.store (0); + fatal << "Cannot create export handler helper thread" << endmsg; + abort(); /* NOTREACHED*/ } } From 301777e7fee181632cc6a20297a0a2879c9bf05f Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 27 Sep 2024 17:45:56 +0200 Subject: [PATCH 33/75] Remove cruft --- libs/ardour/audiosource.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index 6d45a56f2f..823d350bcc 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -423,10 +423,6 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos * least large enough for all the data in the audio file. if it * is too short, assume that a crash or other error truncated * it, and rebuild it from scratch. - * - * XXX this may not work for destructive recording, but we - * might decided to get rid of that anyway. - * */ const off_t expected_file_size = (_length.samples() / (double) samples_per_file_peak) * sizeof (PeakData); @@ -591,10 +587,6 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos samplecnt_t nvisual_peaks = 0; uint32_t i = 0; - /* handle the case where the initial visual peak is on a pixel boundary */ - - //current_stored_peak = min (current_stored_peak, stored_peak_before_next_visual_peak); - /* open ... close during out: handling */ off_t map_off = (uint32_t) (current_stored_peak) * sizeof(PeakData); From c4fdd5356c34210c617241ef16511fcb2950b355 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 27 Sep 2024 23:05:02 +0200 Subject: [PATCH 34/75] Enable debugging for stored threads --- libs/pbd/pthread_utils.cc | 50 ++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc index 052d035535..fee2ea3b7f 100644 --- a/libs/pbd/pthread_utils.cc +++ b/libs/pbd/pthread_utils.cc @@ -28,6 +28,8 @@ #include #endif +#include "pbd/compose.h" +#include "pbd/debug.h" #include "pbd/failed_constructor.h" #include "pbd/pthread_utils.h" @@ -47,10 +49,10 @@ DECLARE_DEFAULT_COMPARISONS (pthread_t) // Needed for 'DECLARE_DEFAULT_COMPARISO using namespace std; -typedef std::list ThreadMap; -static ThreadMap all_threads; -static pthread_mutex_t thread_map_lock = PTHREAD_MUTEX_INITIALIZER; -static Glib::Threads::Private thread_name (free); +typedef std::map ThreadMap; +static ThreadMap all_threads; +static pthread_mutex_t thread_map_lock = PTHREAD_MUTEX_INITIALIZER; +static Glib::Threads::Private thread_name (free); namespace PBD { @@ -94,6 +96,8 @@ fake_thread_start (void* arg) /* name will be deleted by the default handler for GStaticPrivate, when the thread exits */ pthread_set_name (ts->name.c_str ()); + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Started: '%1'\n", ts->name)); + /* we don't need this object anymore */ delete ts; @@ -103,13 +107,13 @@ fake_thread_start (void* arg) /* cleanup */ pthread_mutex_lock (&thread_map_lock); - for (ThreadMap::iterator i = all_threads.begin (); i != all_threads.end (); ++i) { - if (pthread_equal ((*i), pthread_self ())) { - all_threads.erase (i); + for (auto const& t : all_threads) { + if (pthread_equal (t.first, pthread_self ())) { + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Terminated: '%1'\n", t.second)); + all_threads.erase (t.first); break; } } - pthread_mutex_unlock (&thread_map_lock); /* done */ @@ -132,7 +136,7 @@ pthread_create_and_store (string name, pthread_t* thread, void* (*start_routine) if ((ret = pthread_create (thread, &default_attr, fake_thread_start, ts)) == 0) { pthread_mutex_lock (&thread_map_lock); - all_threads.push_back (*thread); + all_threads[*thread] = name; pthread_mutex_unlock (&thread_map_lock); } @@ -171,10 +175,12 @@ void pthread_kill_all (int signum) { pthread_mutex_lock (&thread_map_lock); - for (ThreadMap::iterator i = all_threads.begin (); i != all_threads.end (); ++i) { - if (!pthread_equal ((*i), pthread_self ())) { - pthread_kill ((*i), signum); + for (auto const& t : all_threads) { + if (pthread_equal (t.first, pthread_self ())) { + continue; } + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Kill: '%1'\n", t.second)); + pthread_kill (t.first, signum); } all_threads.clear (); pthread_mutex_unlock (&thread_map_lock); @@ -184,16 +190,12 @@ void pthread_cancel_all () { pthread_mutex_lock (&thread_map_lock); - - for (ThreadMap::iterator i = all_threads.begin (); i != all_threads.end ();) { - ThreadMap::iterator nxt = i; - ++nxt; - - if (!pthread_equal ((*i), pthread_self ())) { - pthread_cancel ((*i)); + for (auto const& t : all_threads) { + if (pthread_equal (t.first, pthread_self ())) { + continue; } - - i = nxt; + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Cancel: '%1'\n", t.second)); + pthread_cancel (t.first); } all_threads.clear (); pthread_mutex_unlock (&thread_map_lock); @@ -203,9 +205,9 @@ void pthread_cancel_one (pthread_t thread) { pthread_mutex_lock (&thread_map_lock); - for (ThreadMap::iterator i = all_threads.begin (); i != all_threads.end (); ++i) { - if (pthread_equal ((*i), thread)) { - all_threads.erase (i); + for (auto const& t : all_threads) { + if (pthread_equal (t.first, thread)) { + all_threads.erase (t.first); break; } } From 8d3ebde60e6d15cf6b8b67f8eadfce0414c5c1ea Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 00:33:27 +0200 Subject: [PATCH 35/75] Distinguish Threads and ThreadName debugging This also allows for -DThread to enable both. We celebrate the 128th debug bit and look forward to the next 64! --- libs/pbd/debug.cc | 3 ++- libs/pbd/pbd/debug.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/pbd/debug.cc b/libs/pbd/debug.cc index d1210b3dff..2fcbc1f510 100644 --- a/libs/pbd/debug.cc +++ b/libs/pbd/debug.cc @@ -61,6 +61,7 @@ DebugBits PBD::DEBUG::Configuration = PBD::new_debug_bit ("configuration"); DebugBits PBD::DEBUG::UndoHistory = PBD::new_debug_bit ("undohistory"); DebugBits PBD::DEBUG::Timing = PBD::new_debug_bit ("timing"); DebugBits PBD::DEBUG::Threads = PBD::new_debug_bit ("threads"); +DebugBits PBD::DEBUG::ThreadName = PBD::new_debug_bit ("threadname"); DebugBits PBD::DEBUG::Locale = PBD::new_debug_bit ("locale"); DebugBits PBD::DEBUG::StringConvert = PBD::new_debug_bit ("stringconvert"); DebugBits PBD::DEBUG::DebugTimestamps = PBD::new_debug_bit ("debugtimestamps"); @@ -103,7 +104,7 @@ PBD::new_debug_bit (const char* name) void PBD::debug_only_print (const char* prefix, string str) { - if ((PBD::debug_bits & DEBUG::Threads).any()) { + if ((PBD::debug_bits & DEBUG::ThreadName).any()) { printf ("0x%lx (%s) ", (intptr_t) DEBUG_THREAD_SELF, pthread_name()); } diff --git a/libs/pbd/pbd/debug.h b/libs/pbd/pbd/debug.h index fdd7f6d503..c00be2d811 100644 --- a/libs/pbd/pbd/debug.h +++ b/libs/pbd/pbd/debug.h @@ -39,7 +39,7 @@ namespace PBD { - typedef std::bitset<128> DebugBits; + typedef std::bitset<192> DebugBits; LIBPBD_API extern DebugBits debug_bits; LIBPBD_API DebugBits new_debug_bit (const char* name); @@ -64,6 +64,7 @@ namespace PBD { LIBPBD_API extern DebugBits UndoHistory; LIBPBD_API extern DebugBits Timing; LIBPBD_API extern DebugBits Threads; + LIBPBD_API extern DebugBits ThreadName; LIBPBD_API extern DebugBits Locale; LIBPBD_API extern DebugBits StringConvert; LIBPBD_API extern DebugBits DebugTimestamps; From 6a741689d1d0d288b9dcb7a7d7753f4b82f0f7eb Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 27 Sep 2024 23:03:26 +0200 Subject: [PATCH 36/75] Consolidate calls to `pthread_create' (1/2) --- gtk2_ardour/linux_vst_gui_support.cc | 21 ++------------------- gtk2_ardour/pingback.cc | 3 +-- gtk2_ardour/strip_silence_dialog.cc | 3 +-- gtk2_ardour/video_image_frame.cc | 2 ++ 4 files changed, 6 insertions(+), 23 deletions(-) diff --git a/gtk2_ardour/linux_vst_gui_support.cc b/gtk2_ardour/linux_vst_gui_support.cc index f2d287c2ea..0aded09abd 100644 --- a/gtk2_ardour/linux_vst_gui_support.cc +++ b/gtk2_ardour/linux_vst_gui_support.cc @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -42,7 +41,6 @@ #include #include #include -#include #include struct ERect{ @@ -319,9 +317,8 @@ any Xevents to all the UI callbacks plugins 'may' have registered on their windows, that is if they don't manage their own UIs **/ static void* -gui_event_loop (void* ptr) +gui_event_loop (void*) { - pthread_set_name ("LXVSTEventLoop"); VSTState* vstfx; int LXVST_sched_timer_interval = 40; //ms, 25fps XEvent event; @@ -509,20 +506,6 @@ int vstfx_init (void* ptr) assert (gui_state == -1); pthread_mutex_init (&plugin_mutex, NULL); - int thread_create_result; - - pthread_attr_t thread_attributes; - - /*Init the attribs to defaults*/ - - pthread_attr_init (&thread_attributes); - - /*Make sure the thread is joinable - this should be the default anyway - - so we can join to it on vstfx_exit*/ - - pthread_attr_setdetachstate (&thread_attributes, PTHREAD_CREATE_JOINABLE); - - /*This is where we need to open a connection to X, and start the GUI thread*/ /*Open our connection to X - all linuxVST plugin UIs handled by the LXVST engine @@ -546,7 +529,7 @@ int vstfx_init (void* ptr) /*Create the thread - use default attrs for now, don't think we need anything special*/ - thread_create_result = pthread_create (&LXVST_gui_event_thread, &thread_attributes, gui_event_loop, NULL); + int thread_create_result = pthread_create_and_store ("LXVSTEventLoop", &LXVST_gui_event_thread, gui_event_loop, NULL, 0); if (thread_create_result != 0) { diff --git a/gtk2_ardour/pingback.cc b/gtk2_ardour/pingback.cc index a1667a09d7..e0a499de3a 100644 --- a/gtk2_ardour/pingback.cc +++ b/gtk2_ardour/pingback.cc @@ -97,7 +97,6 @@ build_query_string (ArdourCurl::HttpGet const & h) static void* _pingback (void *arg) { - pthread_set_name ("Pingback"); ArdourCurl::HttpGet h; //initialize curl @@ -177,7 +176,7 @@ void pingback (const string& version, const string& announce_path) ping_call* cm = new ping_call (version, announce_path); pthread_t thread; - pthread_create_and_store ("pingback", &thread, _pingback, cm); + pthread_create_and_store ("Pingback", &thread, _pingback, cm); } } diff --git a/gtk2_ardour/strip_silence_dialog.cc b/gtk2_ardour/strip_silence_dialog.cc index 6d4fb88a45..c097839b84 100644 --- a/gtk2_ardour/strip_silence_dialog.cc +++ b/gtk2_ardour/strip_silence_dialog.cc @@ -132,7 +132,7 @@ StripSilenceDialog::StripSilenceDialog (Session* s, list const & v) /* Create a thread which runs while the dialogue is open to compute the silence regions */ Completed.connect (_completed_connection, invalidator(*this), boost::bind (&StripSilenceDialog::update, this), gui_context ()); _thread_should_finish = false; - pthread_create (&_thread, 0, StripSilenceDialog::_detection_thread_work, this); + pthread_create_and_store ("SilenceDetect", &_thread, StripSilenceDialog::_detection_thread_work, this, 0); signal_response().connect(sigc::mem_fun (*this, &StripSilenceDialog::finished)); } @@ -248,7 +248,6 @@ void * StripSilenceDialog::_detection_thread_work (void* arg) { StripSilenceDialog* d = reinterpret_cast (arg); - pthread_set_name ("SilenceDetect"); return d->detection_thread_work (); } diff --git a/gtk2_ardour/video_image_frame.cc b/gtk2_ardour/video_image_frame.cc index 33c07b1035..6bd737bf56 100644 --- a/gtk2_ardour/video_image_frame.cc +++ b/gtk2_ardour/video_image_frame.cc @@ -274,6 +274,8 @@ VideoImageFrame::http_get (samplepos_t fn) { queued_request=false; req_video_frame_number=fn; pthread_mutex_unlock(&queue_lock); + /* do not use `pbd pthread_create_and_store' here. + * The request thread uses aync cancellation */ int rv = pthread_create(&thread_id_tt, NULL, http_get_thread, this); thread_active=true; if (rv) { From 538a8cbccc81d4759e78826f6b50deff8afe0990 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 27 Sep 2024 23:04:32 +0200 Subject: [PATCH 37/75] Consolidate calls to `pthread_create' (2/2) --- libs/ardour/ardour/export_handler.h | 3 ++- libs/ardour/control_protocol_manager.cc | 2 +- libs/ardour/export_handler.cc | 15 +++++---------- libs/ardour/session.cc | 6 +++--- libs/ardour/session_process.cc | 4 +--- .../audiographer/sndfile/tmp_file_rt.h | 3 +-- libs/pbd/file_archive.cc | 10 +++++++--- libs/pbd/system_exec.cc | 8 ++------ 8 files changed, 22 insertions(+), 29 deletions(-) diff --git a/libs/ardour/ardour/export_handler.h b/libs/ardour/ardour/export_handler.h index 71c1872632..97479540f6 100644 --- a/libs/ardour/ardour/export_handler.h +++ b/libs/ardour/ardour/export_handler.h @@ -30,6 +30,7 @@ #include #include "pbd/gstdio_compat.h" +#include "pbd/pthread_utils.h" #include "ardour/export_pointers.h" #include "ardour/session.h" @@ -149,7 +150,7 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory, public sigc::tr void timespan_thread_wakeup (); static void* _timespan_thread_run (void*); - pthread_t _timespan_thread; + PBD::Thread* _timespan_thread; std::atomic _timespan_thread_active; pthread_mutex_t _timespan_mutex; pthread_cond_t _timespan_cond; diff --git a/libs/ardour/control_protocol_manager.cc b/libs/ardour/control_protocol_manager.cc index fe570c8dff..43ed625498 100644 --- a/libs/ardour/control_protocol_manager.cc +++ b/libs/ardour/control_protocol_manager.cc @@ -185,7 +185,7 @@ ControlProtocolManager::set_session (Session* s) usb_hotplug_cb, this, &_hpcp)) { _hotplug_thread_run = true; - if (pthread_create (&_hotplug_thread, NULL, usb_hotplug_thread, this)) { + if (pthread_create_and_store ("Ctrl USB Hotplug", &_hotplug_thread, usb_hotplug_thread, this, 0)) { _hotplug_thread_run = false; } } diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc index fe566a0108..91954eccfe 100644 --- a/libs/ardour/export_handler.cc +++ b/libs/ardour/export_handler.cc @@ -25,7 +25,6 @@ #include "pbd/gstdio_compat.h" #include #include -#include #include "pbd/convert.h" @@ -125,7 +124,8 @@ ExportHandler::ExportHandler (Session & session) pthread_mutex_init (&_timespan_mutex, 0); pthread_cond_init (&_timespan_cond, 0); _timespan_thread_active.store (1); - if (pthread_create (&_timespan_thread, NULL, _timespan_thread_run, this)) { + _timespan_thread = PBD::Thread::create (boost::bind (_timespan_thread_run, this), "ExportHandler"); + if (!_timespan_thread) { _timespan_thread_active.store (0); fatal << "Cannot create export handler helper thread" << endmsg; abort(); /* NOTREACHED*/ @@ -145,8 +145,7 @@ ExportHandler::~ExportHandler () pthread_cond_signal (&_timespan_cond); pthread_mutex_unlock (&_timespan_mutex); - void *status; - pthread_join (_timespan_thread, &status); + _timespan_thread->join (); pthread_cond_destroy (&_timespan_cond); pthread_mutex_destroy (&_timespan_mutex); @@ -155,13 +154,10 @@ ExportHandler::~ExportHandler () void* ExportHandler::_timespan_thread_run (void* me) { - char name[64]; - snprintf (name, 64, "Export-TS-%p", (void*)DEBUG_THREAD_SELF); - pthread_set_name (name); ExportHandler* self = static_cast (me); - SessionEvent::create_per_thread_pool (name, 512); - PBD::notify_event_loops_about_thread_creation (pthread_self(), name, 512); + SessionEvent::create_per_thread_pool ("ExportHandler", 512); + PBD::notify_event_loops_about_thread_creation (pthread_self(), "ExportHandler", 512); pthread_mutex_lock (&self->_timespan_mutex); while (self->_timespan_thread_active.load ()) { @@ -178,7 +174,6 @@ ExportHandler::_timespan_thread_run (void* me) } } pthread_mutex_unlock (&self->_timespan_mutex); - pthread_exit (0); return 0; } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 969c2cd88e..6e2b8d6921 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -7845,8 +7845,10 @@ Session::auto_connect_thread_start () lx.release (); _ac_thread_active.store (1); - if (pthread_create (&_auto_connect_thread, NULL, auto_connect_thread, this)) { + if (pthread_create_and_store ("AutoConnect", &_auto_connect_thread, auto_connect_thread, this, 0)) { _ac_thread_active.store (0); + fatal << "Cannot create 'session auto connect' thread" << endmsg; + abort(); /* NOTREACHED*/ } } @@ -7881,9 +7883,7 @@ void * Session::auto_connect_thread (void *arg) { Session *s = static_cast(arg); - pthread_set_name (X_("autoconnect")); s->auto_connect_thread_run (); - pthread_exit (0); return 0; } diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index e3003127a1..1335464731 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -1199,7 +1199,7 @@ Session::emit_thread_start () } _rt_thread_active = true; - if (pthread_create (&_rt_emit_thread, NULL, emit_thread, this)) { + if (pthread_create_and_store ("SessionSignals", &_rt_emit_thread, emit_thread, this, 0)) { _rt_thread_active = false; } } @@ -1225,9 +1225,7 @@ void * Session::emit_thread (void *arg) { Session *s = static_cast(arg); - pthread_set_name ("SessionSignals"); s->emit_thread_run (); - pthread_exit (0); return 0; } diff --git a/libs/audiographer/audiographer/sndfile/tmp_file_rt.h b/libs/audiographer/audiographer/sndfile/tmp_file_rt.h index 52c44f7759..c1c63b8322 100644 --- a/libs/audiographer/audiographer/sndfile/tmp_file_rt.h +++ b/libs/audiographer/audiographer/sndfile/tmp_file_rt.h @@ -138,7 +138,6 @@ class TmpFileRt TmpFileRt *d = static_cast(arg); pthread_set_name ("ExportDiskIO"); d->disk_thread (); - pthread_exit (0); return 0; } @@ -158,7 +157,7 @@ class TmpFileRt pthread_mutex_init (&_disk_thread_lock, 0); pthread_cond_init (&_data_ready, 0); - if (pthread_create (&_thread_id, NULL, _disk_thread, this)) { + if (pthread_create_and_store ("ExportDiskIO", &_thread_id, _disk_thread, this, 0)) { _capture = false; if (SndfileWriter::throw_level (ThrowStrict)) { throw Exception (*this, "Cannot create export disk writer"); diff --git a/libs/pbd/file_archive.cc b/libs/pbd/file_archive.cc index 122d22b6d9..770822d285 100644 --- a/libs/pbd/file_archive.cc +++ b/libs/pbd/file_archive.cc @@ -59,7 +59,6 @@ write_callback (void* buffer, size_t size, size_t nmemb, void* d) static void* get_url (void* arg) { - pthread_set_name ("FileArchiveURL"); FileArchive::Request* r = (FileArchive::Request*) arg; CURL* curl; @@ -328,7 +327,10 @@ std::vector FileArchive::contents_url () { _req.mp.reset (); - pthread_create (&_tid, NULL, get_url, (void*)&_req); + + if (pthread_create_and_store ("FileArchiveHTTP", &_tid, get_url, (void*)&_req, 0)) { + return std::vector (); + } struct archive* a = setup_archive (); archive_read_open (a, (void*)&_req.mp, NULL, ar_read, NULL); @@ -359,7 +361,9 @@ int FileArchive::extract_url () { _req.mp.reset (); - pthread_create (&_tid, NULL, get_url, (void*)&_req); + if (pthread_create_and_store ("FileArchiveHTTP", &_tid, get_url, (void*)&_req)) { + return -1; + } struct archive* a = setup_archive (); archive_read_open (a, (void*)&_req.mp, NULL, ar_read, NULL); int rv = do_extract (a); diff --git a/libs/pbd/system_exec.cc b/libs/pbd/system_exec.cc index 0c100ab5b9..8570dfa598 100644 --- a/libs/pbd/system_exec.cc +++ b/libs/pbd/system_exec.cc @@ -278,9 +278,7 @@ SystemExec::~SystemExec () static void* interposer_thread (void *arg) { SystemExec *sex = static_cast(arg); - pthread_set_name ("ExecStdOut"); sex->output_interposer(); - pthread_exit(0); return 0; } @@ -522,7 +520,7 @@ SystemExec::start (StdErrMode stderr_mode, const char * /*vfork_exec_wrapper*/) return -1; } - int rv = pthread_create (&thread_id_tt, NULL, interposer_thread, this); + int rv = pthread_create_and_store ("ExecStdOut", &thread_id_tt, interposer_thread, this, 0); thread_active=true; if (rv) { thread_active=false; @@ -560,7 +558,6 @@ SystemExec::output_interposer() ReadStdout(rv, bytesRead); /* EMIT SIGNAL */ } Terminated(); /* EMIT SIGNAL */ - pthread_exit(0); } void @@ -808,7 +805,7 @@ SystemExec::start (StdErrMode stderr_mode, const char *vfork_exec_wrapper) close_fd (pout[1]); close_fd (pin[0]); - int rv = pthread_create (&thread_id_tt, NULL, interposer_thread, this); + int rv = pthread_create_and_store ("ExecStdOut", &thread_id_tt, interposer_thread, this, 0); thread_active=true; if (rv) { @@ -917,7 +914,6 @@ again: ReadStdout (rv, r); /* EMIT SIGNAL */ } Terminated (); /* EMIT SIGNAL */ - pthread_exit (0); } void From 88a24ae8e53c00fd5adc47fa8dc0dabe9f784bbe Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 00:14:45 +0200 Subject: [PATCH 38/75] Let PBD::Thread set thread-name --- libs/ardour/audioengine.cc | 6 ++---- libs/ardour/automation_watch.cc | 3 +-- libs/ardour/butler.cc | 3 +-- libs/ardour/midi_patch_manager.cc | 3 +-- libs/ardour/source_factory.cc | 4 +--- libs/ardour/triggerbox.cc | 5 +---- libs/ardour/worker.cc | 4 +--- libs/audiographer/audiographer/sndfile/tmp_file_rt.h | 1 - libs/pbd/base_ui.cc | 3 +-- libs/pbd/downloader.cc | 2 +- libs/pbd/inflater.cc | 2 +- libs/surfaces/cc121/cc121.cc | 2 -- libs/surfaces/contourdesign/contourdesign.cc | 2 -- libs/surfaces/faderport8/faderport8.cc | 2 -- .../generic_midi/generic_midi_control_protocol.cc | 2 -- libs/surfaces/launch_control_xl/launch_control_xl.cc | 2 -- libs/surfaces/mackie/mackie_control_protocol.cc | 2 -- libs/surfaces/maschine2/maschine2.cc | 9 +-------- libs/surfaces/osc/osc.cc | 2 -- libs/surfaces/us2400/us2400_control_protocol.cc | 2 -- libs/surfaces/websockets/ardour_websockets.cc | 1 - libs/surfaces/wiimote/wiimote.cc | 2 -- libs/waveview/wave_view_private.cc | 4 +--- 23 files changed, 13 insertions(+), 55 deletions(-) diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index dd8f4253d4..d46ea648eb 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -678,7 +678,6 @@ void AudioEngine::do_reset_backend() { SessionEvent::create_per_thread_pool (X_("Backend reset processing thread"), 1024); - pthread_set_name ("EngineWatchdog"); Glib::Threads::Mutex::Lock guard (_reset_request_lock); @@ -739,7 +738,6 @@ void AudioEngine::do_devicelist_update() { SessionEvent::create_per_thread_pool (X_("Device list update processing thread"), 512); - pthread_set_name ("DeviceList"); Glib::Threads::Mutex::Lock guard (_devicelist_update_lock); @@ -769,13 +767,13 @@ AudioEngine::start_hw_event_processing() if (_hw_reset_event_thread == 0) { _hw_reset_request_count.store (0); _stop_hw_reset_processing.store (0); - _hw_reset_event_thread = PBD::Thread::create (boost::bind (&AudioEngine::do_reset_backend, this)); + _hw_reset_event_thread = PBD::Thread::create (boost::bind (&AudioEngine::do_reset_backend, this), "EngineWatchdog"); } if (_hw_devicelist_update_thread == 0) { _hw_devicelist_update_count.store (0); _stop_hw_devicelist_processing.store (0); - _hw_devicelist_update_thread = PBD::Thread::create (boost::bind (&AudioEngine::do_devicelist_update, this)); + _hw_devicelist_update_thread = PBD::Thread::create (boost::bind (&AudioEngine::do_devicelist_update, this), "DeviceList"); } } diff --git a/libs/ardour/automation_watch.cc b/libs/ardour/automation_watch.cc index beea168108..3778367e9e 100644 --- a/libs/ardour/automation_watch.cc +++ b/libs/ardour/automation_watch.cc @@ -202,7 +202,6 @@ void AutomationWatch::thread () { pbd_set_thread_priority (pthread_self(), PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority() - 2); // XXX - pthread_set_name ("AutomationWatch"); while (_run_thread) { Glib::usleep ((gulong) floor (Config->get_automation_interval_msecs() * 1000)); // TODO use pthread_cond_timedwait on _run_thread timer (); @@ -224,7 +223,7 @@ AutomationWatch::set_session (Session* s) if (_session) { _run_thread = true; - _thread = PBD::Thread::create (boost::bind (&AutomationWatch::thread, this)); + _thread = PBD::Thread::create (boost::bind (&AutomationWatch::thread, this), "AutomationWatch"); _session->TransportStateChange.connect_same_thread (transport_connection, boost::bind (&AutomationWatch::transport_state_change, this)); } diff --git a/libs/ardour/butler.cc b/libs/ardour/butler.cc index 2a31403a1f..274e2cf5ea 100644 --- a/libs/ardour/butler.cc +++ b/libs/ardour/butler.cc @@ -141,7 +141,7 @@ Butler::start_thread () should_run = false; - if (pthread_create_and_store ("disk butler", &thread, _thread_work, this)) { + if (pthread_create_and_store ("butler", &thread, _thread_work, this)) { error << _("Session: could not create butler thread") << endmsg; return -1; } @@ -171,7 +171,6 @@ void* Butler::_thread_work (void* arg) { SessionEvent::create_per_thread_pool ("butler events", 4096); - pthread_set_name (X_("butler")); /* get thread buffers for RegionFx */ ARDOUR::ProcessThread* pt = new ProcessThread (); pt->get_buffers (); diff --git a/libs/ardour/midi_patch_manager.cc b/libs/ardour/midi_patch_manager.cc index 40d6af221e..8933731fc6 100644 --- a/libs/ardour/midi_patch_manager.cc +++ b/libs/ardour/midi_patch_manager.cc @@ -275,7 +275,6 @@ MidiPatchManager::load_midnams () */ PBD::notify_event_loops_about_thread_creation (pthread_self(), "midi-patch-manager", 8); - pthread_set_name ("MIDNAMLoader"); { PBD::Unwinder npc (no_patch_changed_messages, true); @@ -291,7 +290,7 @@ MidiPatchManager::load_midnams () void MidiPatchManager::load_midnams_in_thread () { - _midnam_load_thread = PBD::Thread::create (boost::bind (&MidiPatchManager::load_midnams, this)); + _midnam_load_thread = PBD::Thread::create (boost::bind (&MidiPatchManager::load_midnams, this), "MIDNAMLoader"); } void diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index 527a09e5b4..841b4cba14 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -65,8 +65,6 @@ static void peak_thread_work () { SessionEvent::create_per_thread_pool (X_("PeakFile Builder "), 64); - pthread_set_name ("PeakFileBuilder"); - while (true) { SourceFactory::peak_building_lock.lock (); @@ -119,7 +117,7 @@ SourceFactory::init () } peak_thread_run = true; for (int n = 0; n < 2; ++n) { - peak_thread_pool.push_back (PBD::Thread::create (&peak_thread_work)); + peak_thread_pool.push_back (PBD::Thread::create (&peak_thread_work, string_compose ("PeakFileBuilder-%1", n))); } } diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index 870b15780c..7656be5a2f 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -4879,7 +4879,7 @@ TriggerBoxThread::TriggerBoxThread () : requests (1024) , _xthread (true) { - if (pthread_create_and_store ("triggerbox thread", &thread, _thread_work, this)) { + if (pthread_create_and_store ("TriggerBox Worker", &thread, _thread_work, this)) { error << _("Session: could not create triggerbox thread") << endmsg; throw failed_constructor (); } @@ -4897,15 +4897,12 @@ void * TriggerBoxThread::_thread_work (void* arg) { SessionEvent::create_per_thread_pool ("tbthread events", 4096); - pthread_set_name (X_("tbthread")); return ((TriggerBoxThread *) arg)->thread_work (); } void * TriggerBoxThread::thread_work () { - pthread_set_name (X_("Trigger Worker")); - while (true) { char msg; diff --git a/libs/ardour/worker.cc b/libs/ardour/worker.cc index 1fc385c958..e8fbe3abfd 100644 --- a/libs/ardour/worker.cc +++ b/libs/ardour/worker.cc @@ -41,7 +41,7 @@ Worker::Worker(Workee* workee, uint32_t ring_size, bool threaded) , _synchronous(!threaded) { if (threaded) { - _thread = PBD::Thread::create (boost::bind (&Worker::run, this)); + _thread = PBD::Thread::create (boost::bind (&Worker::run, this), "LV2Worker"); } } @@ -137,8 +137,6 @@ Worker::emit_responses() void Worker::run() { - pthread_set_name ("LV2Worker"); - void* buf = NULL; size_t buf_size = 0; while (true) { diff --git a/libs/audiographer/audiographer/sndfile/tmp_file_rt.h b/libs/audiographer/audiographer/sndfile/tmp_file_rt.h index c1c63b8322..4b9f321b4e 100644 --- a/libs/audiographer/audiographer/sndfile/tmp_file_rt.h +++ b/libs/audiographer/audiographer/sndfile/tmp_file_rt.h @@ -136,7 +136,6 @@ class TmpFileRt static void * _disk_thread (void *arg) { TmpFileRt *d = static_cast(arg); - pthread_set_name ("ExportDiskIO"); d->disk_thread (); return 0; } diff --git a/libs/pbd/base_ui.cc b/libs/pbd/base_ui.cc index 9c0a9a264d..d6b37f3657 100644 --- a/libs/pbd/base_ui.cc +++ b/libs/pbd/base_ui.cc @@ -94,7 +94,6 @@ BaseUI::set_thread_priority () const void BaseUI::main_thread () { - pthread_set_name (string_compose ("UI:%1", event_loop_name ()).c_str ()); DEBUG_TRACE (DEBUG::EventLoop, string_compose ("%1: event loop running in thread %2\n", event_loop_name(), pthread_name())); set_event_loop_for_thread (this); thread_init (); @@ -122,7 +121,7 @@ BaseUI::run () attach_request_source (); Glib::Threads::Mutex::Lock lm (_run_lock); - _run_loop_thread = PBD::Thread::create (boost::bind (&BaseUI::main_thread, this)); + _run_loop_thread = PBD::Thread::create (boost::bind (&BaseUI::main_thread, this), string_compose ("UI:%1", event_loop_name ())); _running.wait (_run_lock); } diff --git a/libs/pbd/downloader.cc b/libs/pbd/downloader.cc index 57ff9e5ca7..9acf3ff360 100644 --- a/libs/pbd/downloader.cc +++ b/libs/pbd/downloader.cc @@ -98,7 +98,7 @@ Downloader::start () _cancel = false; _status = 0; /* unknown at this point */ - return 0 != (thread = PBD::Thread::create (boost::bind (&Downloader::download, this))); + return 0 != (thread = PBD::Thread::create (boost::bind (&Downloader::download, this), "Downloader")); } void diff --git a/libs/pbd/inflater.cc b/libs/pbd/inflater.cc index 35f9b2a1df..4a747f8ffe 100644 --- a/libs/pbd/inflater.cc +++ b/libs/pbd/inflater.cc @@ -45,7 +45,7 @@ Inflater::~Inflater () int Inflater::start () { - return 0 != (thread = PBD::Thread::create (boost::bind (&Inflater::threaded_inflate, this))); + return 0 != (thread = PBD::Thread::create (boost::bind (&Inflater::threaded_inflate, this), "Inflater")); } void diff --git a/libs/surfaces/cc121/cc121.cc b/libs/surfaces/cc121/cc121.cc index 1debf8e69c..f5360f5007 100644 --- a/libs/surfaces/cc121/cc121.cc +++ b/libs/surfaces/cc121/cc121.cc @@ -258,8 +258,6 @@ CC121::stop () void CC121::thread_init () { - pthread_set_name (event_loop_name().c_str()); - PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048); ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128); diff --git a/libs/surfaces/contourdesign/contourdesign.cc b/libs/surfaces/contourdesign/contourdesign.cc index a7fa14b4b4..21f13d856f 100644 --- a/libs/surfaces/contourdesign/contourdesign.cc +++ b/libs/surfaces/contourdesign/contourdesign.cc @@ -238,8 +238,6 @@ void ContourDesignControlProtocol::thread_init () { DEBUG_TRACE (DEBUG::ContourDesignControl, "thread_init()\n"); - - pthread_set_name (X_("contourdesign")); PBD::notify_event_loops_about_thread_creation (pthread_self (), X_("contourdesign"), 2048); ARDOUR::SessionEvent::create_per_thread_pool (X_("contourdesign"), 128); diff --git a/libs/surfaces/faderport8/faderport8.cc b/libs/surfaces/faderport8/faderport8.cc index bbf77723db..852697f311 100644 --- a/libs/surfaces/faderport8/faderport8.cc +++ b/libs/surfaces/faderport8/faderport8.cc @@ -283,8 +283,6 @@ FaderPort8::stop () void FaderPort8::thread_init () { - pthread_set_name (event_loop_name().c_str()); - PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048); ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128); diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc index 697f2c8c10..612502964d 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc @@ -308,8 +308,6 @@ GenericMidiControlProtocol::stop () void GenericMidiControlProtocol::thread_init () { - pthread_set_name (event_loop_name().c_str()); - PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048); ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128); diff --git a/libs/surfaces/launch_control_xl/launch_control_xl.cc b/libs/surfaces/launch_control_xl/launch_control_xl.cc index acf04b5717..9e15164820 100644 --- a/libs/surfaces/launch_control_xl/launch_control_xl.cc +++ b/libs/surfaces/launch_control_xl/launch_control_xl.cc @@ -665,8 +665,6 @@ void LaunchControlXL::handle_midi_note_off_message(MIDI::Parser & parser, MIDI:: void LaunchControlXL::thread_init () { - pthread_set_name (event_loop_name().c_str()); - PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048); ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128); diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index 7e34712c1c..d372da6265 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -194,8 +194,6 @@ MackieControlProtocol::~MackieControlProtocol() void MackieControlProtocol::thread_init () { - pthread_set_name (event_loop_name().c_str()); - PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048); ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128); diff --git a/libs/surfaces/maschine2/maschine2.cc b/libs/surfaces/maschine2/maschine2.cc index e2bc32fbbe..616f3eab2e 100644 --- a/libs/surfaces/maschine2/maschine2.cc +++ b/libs/surfaces/maschine2/maschine2.cc @@ -307,16 +307,9 @@ Maschine2::stop () void Maschine2::thread_init () { - pthread_set_name (event_loop_name().c_str()); ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 1024); PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 1024); - - struct sched_param rtparam; - memset (&rtparam, 0, sizeof (rtparam)); - rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */ - if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) { - // do we care? not particularly. - } + set_thread_priority (); } void diff --git a/libs/surfaces/osc/osc.cc b/libs/surfaces/osc/osc.cc index f1b6a8f9d6..6e30b6b139 100644 --- a/libs/surfaces/osc/osc.cc +++ b/libs/surfaces/osc/osc.cc @@ -284,8 +284,6 @@ OSC::start () void OSC::thread_init () { - pthread_set_name (event_loop_name().c_str()); - if (_osc_unix_server) { Glib::RefPtr src = IOSource::create (lo_server_get_socket_fd (_osc_unix_server), IO_IN|IO_HUP|IO_ERR); src->connect (sigc::bind (sigc::mem_fun (*this, &OSC::osc_input_handler), _osc_unix_server)); diff --git a/libs/surfaces/us2400/us2400_control_protocol.cc b/libs/surfaces/us2400/us2400_control_protocol.cc index 57f52a3cd5..101b41bd3d 100644 --- a/libs/surfaces/us2400/us2400_control_protocol.cc +++ b/libs/surfaces/us2400/us2400_control_protocol.cc @@ -174,8 +174,6 @@ US2400Protocol::~US2400Protocol() void US2400Protocol::thread_init () { - pthread_set_name (event_loop_name().c_str()); - PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048); ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128); diff --git a/libs/surfaces/websockets/ardour_websockets.cc b/libs/surfaces/websockets/ardour_websockets.cc index 9b8ec442ef..5fec5d5819 100644 --- a/libs/surfaces/websockets/ardour_websockets.cc +++ b/libs/surfaces/websockets/ardour_websockets.cc @@ -76,7 +76,6 @@ ArdourWebsockets::set_active (bool yn) void ArdourWebsockets::thread_init () { - pthread_set_name (event_loop_name ().c_str ()); PBD::notify_event_loops_about_thread_creation (pthread_self (), event_loop_name (), 2048); SessionEvent::create_per_thread_pool (event_loop_name (), 128); } diff --git a/libs/surfaces/wiimote/wiimote.cc b/libs/surfaces/wiimote/wiimote.cc index b92d0901fc..388161ad84 100644 --- a/libs/surfaces/wiimote/wiimote.cc +++ b/libs/surfaces/wiimote/wiimote.cc @@ -154,8 +154,6 @@ WiimoteControlProtocol::thread_init () { DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::thread_init init\n"); - pthread_set_name (X_("wiimote")); - // allow to make requests to the GUI and RT thread(s) PBD::notify_event_loops_about_thread_creation (pthread_self (), X_("wiimote"), 2048); BasicUI::register_thread ("wiimote"); diff --git a/libs/waveview/wave_view_private.cc b/libs/waveview/wave_view_private.cc index e1bcb2d783..eb68bfbfe5 100644 --- a/libs/waveview/wave_view_private.cc +++ b/libs/waveview/wave_view_private.cc @@ -405,7 +405,7 @@ WaveViewDrawingThread::start () { assert (!_thread); - _thread = PBD::Thread::create (&WaveViewThreads::thread_proc); + _thread = PBD::Thread::create (&WaveViewThreads::thread_proc, "WaveViewDrawing"); } void @@ -477,8 +477,6 @@ WaveViewThreads::thread_proc () void WaveViewThreads::_thread_proc () { - pthread_set_name ("WaveViewDrawing"); - while (true) { _queue_mutex.lock (); From e8445d13ec741644062adf5d041422fec448774b Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 00:15:12 +0200 Subject: [PATCH 39/75] Require thread name to be passed to PBD::Thread and store thread --- libs/pbd/pbd/pthread_utils.h | 2 +- libs/pbd/pthread_utils.cc | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/libs/pbd/pbd/pthread_utils.h b/libs/pbd/pbd/pthread_utils.h index e1b18f2603..862340c548 100644 --- a/libs/pbd/pbd/pthread_utils.h +++ b/libs/pbd/pbd/pthread_utils.h @@ -97,7 +97,7 @@ namespace PBD { class LIBPBD_API Thread { public: - static Thread* create (boost::function const&, std::string const& name = ""); + static Thread* create (boost::function const&, std::string const& name); static Thread* self (); void join (); bool caller_is_self () const; diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc index fee2ea3b7f..314fd7209c 100644 --- a/libs/pbd/pthread_utils.cc +++ b/libs/pbd/pthread_utils.cc @@ -486,6 +486,12 @@ PBD::Thread::Thread (boost::function const& slot, std::string const& na if (pthread_create (&_t, &thread_attributes, _run, this)) { throw failed_constructor (); } + + if (_joinable) { + pthread_mutex_lock (&thread_map_lock); + all_threads[_t] = name; + pthread_mutex_unlock (&thread_map_lock); + } } void* @@ -494,8 +500,22 @@ PBD::Thread::_run (void* arg) { if (!self->_name.empty ()) { pthread_set_name (self->_name.c_str ()); } + + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Started: '%1'\n", self->_name)); + self->_slot (); + /* cleanup */ + pthread_mutex_lock (&thread_map_lock); + for (auto const& t : all_threads) { + if (pthread_equal (t.first, pthread_self ())) { + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Terminated: '%1'\n", t.second)); + all_threads.erase (t.first); + break; + } + } + pthread_mutex_unlock (&thread_map_lock); + pthread_exit (0); return 0; } From e8c67408bb75ce135a18f62741493d8a8892d555 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 01:25:16 +0200 Subject: [PATCH 40/75] Impose stack limit on backend threads (freewheeling, MIDI poll) --- libs/backends/alsa/alsa_audiobackend.cc | 2 +- libs/backends/coreaudio/coreaudio_backend.cc | 2 +- libs/backends/portaudio/portaudio_backend.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/backends/alsa/alsa_audiobackend.cc b/libs/backends/alsa/alsa_audiobackend.cc index 9f6c1f7c4c..67794bef9d 100644 --- a/libs/backends/alsa/alsa_audiobackend.cc +++ b/libs/backends/alsa/alsa_audiobackend.cc @@ -1385,7 +1385,7 @@ AlsaAudioBackend::midi_device_thread () bool AlsaAudioBackend::listen_for_midi_device_changes () { - if (pthread_create (&_midi_device_thread_id, NULL, _midi_device_thread, this)) { + if (pbd_pthread_create (PBD_RT_STACKSIZE_HELP, &_midi_device_thread_id, _midi_device_thread, this)) { return false; } return true; diff --git a/libs/backends/coreaudio/coreaudio_backend.cc b/libs/backends/coreaudio/coreaudio_backend.cc index bed80696b3..a81b7d82d6 100644 --- a/libs/backends/coreaudio/coreaudio_backend.cc +++ b/libs/backends/coreaudio/coreaudio_backend.cc @@ -605,7 +605,7 @@ CoreAudioBackend::_start (bool for_latency_measurement) return PortReconnectError; } - if (pthread_create (&_freeewheel_thread, NULL, pthread_freewheel, this)) + if (pbd_pthread_create (PBD_RT_STACKSIZE_PROC, &_freeewheel_thread, pthread_freewheel, this)) { PBD::error << _("CoreAudioBackend: failed to create process thread.") << endmsg; delete _pcmio; _pcmio = 0; diff --git a/libs/backends/portaudio/portaudio_backend.cc b/libs/backends/portaudio/portaudio_backend.cc index 3295bb0e79..c8268a5045 100644 --- a/libs/backends/portaudio/portaudio_backend.cc +++ b/libs/backends/portaudio/portaudio_backend.cc @@ -856,7 +856,7 @@ static void* freewheel_thread(void* arg) bool PortAudioBackend::start_freewheel_process_thread () { - if (pthread_create(&_pthread_freewheel, NULL, freewheel_thread, this)) { + if (pbd_pthread_create (PBD_RT_STACKSIZE_PROC, &_pthread_freewheel, freewheel_thread, this)) { DEBUG_AUDIO("Failed to create main audio thread\n"); return false; } From aeb4f925c6f4313947a1c4abfa038d1ecf50626b Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 02:46:23 +0200 Subject: [PATCH 41/75] PBD pthreads: allow unlimited stacksize --- libs/pbd/pthread_utils.cc | 82 ++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc index 314fd7209c..43423851d5 100644 --- a/libs/pbd/pthread_utils.cc +++ b/libs/pbd/pthread_utils.cc @@ -74,6 +74,42 @@ PBD::notify_event_loops_about_thread_creation (pthread_t thread, const std::stri ThreadCreatedWithRequestSize (thread, emitting_thread_name, request_count); } +static size_t +pbd_stack_size () +{ + size_t rv = 0; +#if !defined PLATFORM_WINDOWS && defined __GLIBC__ + + size_t pt_min_stack = 16384; + +#ifdef PTHREAD_STACK_MIN + pt_min_stack = PTHREAD_STACK_MIN; +#endif + + void* handle = dlopen (NULL, RTLD_LAZY); + + /* This function is internal (it has a GLIBC_PRIVATE) version, but + * available via weak symbol, or dlsym, and returns + * + * GLRO(dl_pagesize) + __static_tls_size + PTHREAD_STACK_MIN + */ + + size_t (*__pthread_get_minstack) (const pthread_attr_t* attr) = + (size_t (*) (const pthread_attr_t*))dlsym (handle, "__pthread_get_minstack"); + + if (__pthread_get_minstack != NULL) { + pthread_attr_t attr; + pthread_attr_init (&attr); + rv = __pthread_get_minstack (&attr); + assert (rv >= pt_min_stack); + rv -= pt_min_stack; + pthread_attr_destroy (&attr); + } + dlclose (handle); +#endif + return rv; +} + struct ThreadStartWithName { void* (*thread_work) (void*); void* arg; @@ -129,7 +165,7 @@ pthread_create_and_store (string name, pthread_t* thread, void* (*start_routine) /* set default stack size to sensible default for memlocking */ pthread_attr_init (&default_attr); if (stacklimit > 0) { - pthread_attr_setstacksize (&default_attr, stacklimit); + pthread_attr_setstacksize (&default_attr, stacklimit + pbd_stack_size ()); } ThreadStartWithName* ts = new ThreadStartWithName (start_routine, arg, name); @@ -216,42 +252,6 @@ pthread_cancel_one (pthread_t thread) pthread_mutex_unlock (&thread_map_lock); } -static size_t -pbd_stack_size () -{ - size_t rv = 0; -#if !defined PLATFORM_WINDOWS && defined __GLIBC__ - - size_t pt_min_stack = 16384; - -#ifdef PTHREAD_STACK_MIN - pt_min_stack = PTHREAD_STACK_MIN; -#endif - - void* handle = dlopen (NULL, RTLD_LAZY); - - /* This function is internal (it has a GLIBC_PRIVATE) version, but - * available via weak symbol, or dlsym, and returns - * - * GLRO(dl_pagesize) + __static_tls_size + PTHREAD_STACK_MIN - */ - - size_t (*__pthread_get_minstack) (const pthread_attr_t* attr) = - (size_t (*) (const pthread_attr_t*))dlsym (handle, "__pthread_get_minstack"); - - if (__pthread_get_minstack != NULL) { - pthread_attr_t attr; - pthread_attr_init (&attr); - rv = __pthread_get_minstack (&attr); - assert (rv >= pt_min_stack); - rv -= pt_min_stack; - pthread_attr_destroy (&attr); - } - dlclose (handle); -#endif - return rv; -} - int pbd_pthread_create ( const size_t stacksize, @@ -263,7 +263,9 @@ pbd_pthread_create ( pthread_attr_t attr; pthread_attr_init (&attr); - pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ()); + if (stacksize > 0) { + pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ()); + } rv = pthread_create (thread, &attr, start_routine, arg); pthread_attr_destroy (&attr); return rv; @@ -358,7 +360,9 @@ pbd_realtime_pthread_create ( pthread_attr_setschedparam (&attr, &parm); pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED); - pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ()); + if (stacksize > 0) { + pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ()); + } rv = pthread_create (thread, &attr, start_routine, arg); pthread_attr_destroy (&attr); return rv; From dcd79f313546aba447243f77ff143d48cd407bd3 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 03:05:10 +0200 Subject: [PATCH 42/75] Debug Backend thread creation --- libs/pbd/pthread_utils.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc index 43423851d5..9655665988 100644 --- a/libs/pbd/pthread_utils.cc +++ b/libs/pbd/pthread_utils.cc @@ -266,6 +266,7 @@ pbd_pthread_create ( if (stacksize > 0) { pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ()); } + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Start Non-RT Thread stacksize = 0x%1%2\n", std::hex, stacksize)); rv = pthread_create (thread, &attr, start_routine, arg); pthread_attr_destroy (&attr); return rv; @@ -363,6 +364,7 @@ pbd_realtime_pthread_create ( if (stacksize > 0) { pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ()); } + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Start Realtime Thread policy = %1 priority = %2 stacksize = 0x%3%4\n", policy, priority, std::hex, stacksize)); rv = pthread_create (thread, &attr, start_routine, arg); pthread_attr_destroy (&attr); return rv; From d089f38481a43800e8cb84dfa9df7ec8e752b300 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 02:48:15 +0200 Subject: [PATCH 43/75] Use config variable to set IOTask thread policy --- libs/ardour/ardour/rc_configuration_vars.h | 1 + libs/ardour/io_tasklist.cc | 56 +++++++++------------- 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/libs/ardour/ardour/rc_configuration_vars.h b/libs/ardour/ardour/rc_configuration_vars.h index f58ae85bba..d476d557b8 100644 --- a/libs/ardour/ardour/rc_configuration_vars.h +++ b/libs/ardour/ardour/rc_configuration_vars.h @@ -223,6 +223,7 @@ CONFIG_VARIABLE (bool, allow_special_bus_removal, "allow-special-bus-removal", f 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, io_thread_count, "io-thread-count", -2) +CONFIG_VARIABLE (int32_t, io_thread_policy, "io-thread-policy", 0) 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_templates, "max-recent-templates", 10) diff --git a/libs/ardour/io_tasklist.cc b/libs/ardour/io_tasklist.cc index 5e815c5faa..6fa338cbda 100644 --- a/libs/ardour/io_tasklist.cc +++ b/libs/ardour/io_tasklist.cc @@ -32,6 +32,7 @@ #include "ardour/disk_reader.h" #include "ardour/io_tasklist.h" #include "ardour/process_thread.h" +#include "ardour/rc_configuration.h" #include "ardour/session_event.h" #include "pbd/i18n.h" @@ -50,54 +51,41 @@ IOTaskList::IOTaskList (uint32_t n_threads) return; } - pthread_attr_t attr; - struct sched_param parm; + bool use_rt; + int policy; - pthread_attr_init (&attr); - - bool use_sched_param = true; - int policy = SCHED_RR; - - const char* p = getenv ("ARDOUR_IO_SCHED"); - if (p) { - int pi = atoi (p); - if (pi < 0) { - policy = SCHED_RR; - } else if (pi > 0) { + switch (Config->get_io_thread_policy ()) { + case 1: + use_rt = true; policy = SCHED_FIFO; - } else { - use_sched_param = false; - } + break; + case 2: + use_rt = true; + policy = SCHED_RR; + default: + use_rt = false; + policy = SCHED_OTHER; + break; } - if (use_sched_param) { - parm.sched_priority = pbd_absolute_rt_priority (SCHED_RR, pbd_pthread_priority (THREAD_IO)); #ifdef PLATFORM_WINDOWS - pthread_attr_setschedpolicy (&attr, SCHED_OTHER); -#else - pthread_attr_setschedpolicy (&attr, policy); + policy = SCHED_OTHER; #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, policy = %3\n", _n_threads, parm.sched_priority, policy)); - } else { - DEBUG_TRACE (PBD::DEBUG::IOTaskList, string_compose ("IOTaskList starting %1 threads with default priority.\n", _n_threads)); - } + + DEBUG_TRACE (PBD::DEBUG::IOTaskList, string_compose ("IOTaskList starting %1 threads with sched policy = %2\n", _n_threads, policy)); _workers.resize (_n_threads); for (uint32_t i = 0; i < _n_threads; ++i) { - if (pthread_create (&_workers[i], &attr, &_worker_thread, this)) { - if (pthread_create (&_workers[i], NULL, &_worker_thread, this)) { + if (!use_rt || pbd_realtime_pthread_create (policy, THREAD_IO, 0, &_workers[i], &_worker_thread, this)) { + if (use_rt && i == 0) { + PBD::warning << _("IOTaskList: cannot acquire realtime permissions.") << endmsg; + } + if (pbd_pthread_create (0, &_workers[i], &_worker_thread, this)) { std::cerr << "Failed to start IOTaskList thread\n"; throw failed_constructor (); } - if (i == 0) { - PBD::warning << _("IOTaskList: cannot acquire realtime permissions.") << endmsg; - } } } - pthread_attr_destroy (&attr); } IOTaskList::~IOTaskList () From 0b5a197f7660a89abe5a10a52114ddb7536b8de8 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 02:48:26 +0200 Subject: [PATCH 44/75] NO-OP: whitespace --- libs/ardour/io_tasklist.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/ardour/io_tasklist.cc b/libs/ardour/io_tasklist.cc index 6fa338cbda..faa0b02cca 100644 --- a/libs/ardour/io_tasklist.cc +++ b/libs/ardour/io_tasklist.cc @@ -146,8 +146,8 @@ IOTaskList::_worker_thread (void* me) #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); + // 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 (); From 395833e4f8ef224777a1be86faa3f8d980a8d542 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 03:49:59 +0200 Subject: [PATCH 45/75] Always use PBD API for thread priorities --- libs/ardour/automation_watch.cc | 2 +- libs/ardour/convolver.cc | 2 +- libs/ardour/globals.cc | 2 +- libs/ardour/lv2_plugin.cc | 2 +- libs/pbd/base_ui.cc | 2 +- libs/pbd/pbd/pthread_utils.h | 4 ++++ 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libs/ardour/automation_watch.cc b/libs/ardour/automation_watch.cc index 3778367e9e..20b6b093a9 100644 --- a/libs/ardour/automation_watch.cc +++ b/libs/ardour/automation_watch.cc @@ -201,7 +201,7 @@ AutomationWatch::timer () void AutomationWatch::thread () { - pbd_set_thread_priority (pthread_self(), PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority() - 2); // XXX + pbd_set_thread_priority (pthread_self(), PBD_SCHED_FIFO, PBD_RT_PRI_CTRL); while (_run_thread) { Glib::usleep ((gulong) floor (Config->get_automation_interval_msecs() * 1000)); // TODO use pthread_cond_timedwait on _run_thread timer (); diff --git a/libs/ardour/convolver.cc b/libs/ardour/convolver.cc index ee4fff453d..9dd1dfecf6 100644 --- a/libs/ardour/convolver.cc +++ b/libs/ardour/convolver.cc @@ -169,7 +169,7 @@ Convolution::restart () } if (rv == 0) { - rv = _convproc.start_process (pbd_absolute_rt_priority (PBD_SCHED_FIFO, AudioEngine::instance ()->client_real_time_priority () - 1), PBD_SCHED_FIFO); + rv = _convproc.start_process (pbd_absolute_rt_priority (PBD_SCHED_FIFO, PBD_RT_PRI_PROC), PBD_SCHED_FIFO); } assert (rv == 0); // bail out in debug builds diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 22166d7eea..a017ff7d3b 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -815,7 +815,7 @@ ARDOUR::init_post_engine (uint32_t start_cnt) } } - BaseUI::set_thread_priority (pbd_absolute_rt_priority (PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority () - 2)); + BaseUI::set_thread_priority (pbd_absolute_rt_priority (PBD_SCHED_FIFO, PBD_RT_PRI_CTRL)); TransportMasterManager::instance ().restart (); } diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 1121334159..de01e94245 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -607,7 +607,7 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate) static const int32_t _min_block_length = 1; // may happen during split-cycles 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_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, PBD_RT_PRI_PROC); static const int32_t hw_concurrency = how_many_dsp_threads (); /* Consider updating max-block-size whenever the buffersize changes. * It requires re-instantiating the plugin (which is a non-realtime operation), diff --git a/libs/pbd/base_ui.cc b/libs/pbd/base_ui.cc index d6b37f3657..4b4a65edb7 100644 --- a/libs/pbd/base_ui.cc +++ b/libs/pbd/base_ui.cc @@ -50,7 +50,7 @@ using namespace PBD; using namespace Glib; uint64_t BaseUI::rt_bit = 1; -int BaseUI::_thread_priority = PBD_RT_PRI_PROC - 1; +int BaseUI::_thread_priority = PBD_RT_PRI_CTRL; BaseUI::RequestType BaseUI::CallSlot = BaseUI::new_request_type(); BaseUI::RequestType BaseUI::Quit = BaseUI::new_request_type(); diff --git a/libs/pbd/pbd/pthread_utils.h b/libs/pbd/pbd/pthread_utils.h index 862340c548..a528363230 100644 --- a/libs/pbd/pbd/pthread_utils.h +++ b/libs/pbd/pbd/pthread_utils.h @@ -57,6 +57,7 @@ # define PBD_RT_PRI_MAIN pbd_pthread_priority (THREAD_MAIN) # define PBD_RT_PRI_MIDI pbd_pthread_priority (THREAD_MIDI) # define PBD_RT_PRI_PROC pbd_pthread_priority (THREAD_PROC) +# define PBD_RT_PRI_CTRL pbd_pthread_priority (THREAD_CTRL) LIBPBD_API int pthread_create_and_store (std::string name, pthread_t *thread, void * (*start_routine)(void *), void * arg, uint32_t stacklimit = 0x80000 /*512kB*/); LIBPBD_API void pthread_cancel_one (pthread_t thread); @@ -65,10 +66,13 @@ LIBPBD_API void pthread_kill_all (int signum); LIBPBD_API const char* pthread_name (); LIBPBD_API void pthread_set_name (const char* name); +LIBPBD_API void pbd_set_engine_rt_priority (int); + enum PBDThreadClass { THREAD_MAIN, // main audio I/O thread THREAD_MIDI, // MIDI I/O threads THREAD_PROC, // realtime worker + THREAD_CTRL, // Automation watch, BaseUI THREAD_IO // non-realtime I/O }; From 72deb74c58a0403c652dfbb87aad597c7f3244cf Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 04:15:08 +0200 Subject: [PATCH 46/75] Unconditionally check for mlock Prepare for `is_realtime` API removal. All backend except for Dummy and sometimes JACK are realtime anyway. --- gtk2_ardour/ardour_ui_startup.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk2_ardour/ardour_ui_startup.cc b/gtk2_ardour/ardour_ui_startup.cc index a2ac3b953b..e22db6cd6b 100644 --- a/gtk2_ardour/ardour_ui_startup.cc +++ b/gtk2_ardour/ardour_ui_startup.cc @@ -758,7 +758,7 @@ ARDOUR_UI::check_memory_locking () XMLNode* memory_warning_node = Config->instant_xml (X_("no-memory-warning")); - if (AudioEngine::instance()->is_realtime() && memory_warning_node == 0) { + if (memory_warning_node == 0) { struct rlimit limits; int64_t ram; From dd4a1a6d73b2fd5fa293361cc4a1d43d1be63b5b Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 03:59:37 +0200 Subject: [PATCH 47/75] Set thread priority relative to backend This also removed direct calls to backend real_time_priority for good measure. --- libs/ardour/ardour/audio_backend.h | 2 +- libs/ardour/ardour/audioengine.h | 3 --- libs/ardour/audioengine.cc | 38 +++++---------------------- libs/pbd/pthread_utils.cc | 41 +++++++++++++++++------------- 4 files changed, 30 insertions(+), 54 deletions(-) diff --git a/libs/ardour/ardour/audio_backend.h b/libs/ardour/ardour/audio_backend.h index e775bec59f..5986b895e4 100644 --- a/libs/ardour/ardour/audio_backend.h +++ b/libs/ardour/ardour/audio_backend.h @@ -164,7 +164,7 @@ public: /** Return true if the backed is JACK */ virtual bool is_jack () const { return false; } - virtual int client_real_time_priority () { return PBD_RT_PRI_PROC; } + virtual int client_real_time_priority () { return 0; } /* Discovering devices and parameters */ diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 531ae315a2..cc1b0db6bc 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -115,9 +115,6 @@ class LIBARDOUR_API AudioEngine : public PortManager, public SessionHandlePtr void request_device_list_update(); void launch_device_control_app(); - int client_real_time_priority (); - bool is_realtime() const; - // for the user which hold state_lock to check if reset operation is pending bool is_reset_requested() const { return _hw_reset_request_count.load(); } diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index d46ea648eb..019dd3b7cf 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -1067,6 +1067,12 @@ AudioEngine::start (bool for_latency) return -1; } + if (_backend->is_realtime ()) { + pbd_set_engine_rt_priority (_backend->client_real_time_priority ()); + } else { + pbd_set_engine_rt_priority (0); + } + _running = true; if (_session) { @@ -1190,38 +1196,6 @@ AudioEngine::get_dsp_load() const return _backend->dsp_load (); } -bool -AudioEngine::is_realtime() const -{ - if (!_backend) { - return false; - } - - return _backend->is_realtime(); -} - -int -AudioEngine::client_real_time_priority () -{ - if (!_backend) { - assert (0); - return PBD_RT_PRI_PROC; - } - if (!_backend->is_realtime ()) { - /* this is only an issue with the Dummy backend. - * - with JACK, we require rt permissions. - * - with ALSA/PulseAudio this can only happen if rt permissions - * are n/a. Other attempts to get rt will fail likewise. - * - * perhaps: - * TODO: use is_realtime () ? PBD_SCHED_FIFO : PBD_SCHED_OTHER - */ - return PBD_RT_PRI_PROC; // XXX - } - - return _backend->client_real_time_priority(); -} - void AudioEngine::transport_start () { diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc index 9655665988..f798da4d8a 100644 --- a/libs/pbd/pthread_utils.cc +++ b/libs/pbd/pthread_utils.cc @@ -53,6 +53,7 @@ typedef std::map ThreadMap; static ThreadMap all_threads; static pthread_mutex_t thread_map_lock = PTHREAD_MUTEX_INITIALIZER; static Glib::Threads::Private thread_name (free); +static int base_priority_relative_to_max = -20; namespace PBD { @@ -272,6 +273,19 @@ pbd_pthread_create ( return rv; } +void +pbd_set_engine_rt_priority (int p) +{ + /* this is mainly for JACK's benefit */ + const int p_max = sched_get_priority_max (SCHED_FIFO); + const int p_min = sched_get_priority_min (SCHED_FIFO); + if (p <= 0 || p <= p_min + 10 || p > p_max) { + base_priority_relative_to_max = -20; + } else { + base_priority_relative_to_max = p - p_max; + } +} + int pbd_pthread_priority (PBDThreadClass which) { @@ -292,11 +306,11 @@ pbd_pthread_priority (PBDThreadClass which) return -13; } #else - int base = -20; + int base = base_priority_relative_to_max; const char* p = getenv ("ARDOUR_SCHED_PRI"); if (p && *p) { base = atoi (p); - if (base > -5 && base < 5) { + if (base > -5 || base < -85) { base = -20; } } @@ -309,6 +323,8 @@ pbd_pthread_priority (PBDThreadClass which) default: case THREAD_PROC: return base - 2; + case THREAD_CTRL: + return base - 3; case THREAD_IO: return base - 10; } @@ -322,23 +338,12 @@ pbd_absolute_rt_priority (int policy, int priority) const int p_min = sched_get_priority_min (policy); // Linux: 1 const int p_max = sched_get_priority_max (policy); // Linux: 99 - if (priority == 0) { - assert (0); - priority = (p_min + p_max) / 2; - } else if (priority > 0) { - /* value relative to minium */ - priority += p_min - 1; - } else { - /* value relative maximum */ - priority += p_max + 1; - } + /* priority is relative to the max */ + assert (priority < 0); + priority += p_max + 1; - if (priority > p_max) { - priority = p_max; - } - if (priority < p_min) { - priority = p_min; - } + priority = std::min (p_max, priority); + priority = std::max (p_min, priority); return priority; } From 2014faaeca123b07b95efdec40987eef82f55382 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 14:45:05 +0200 Subject: [PATCH 48/75] Fix BaseUI thread priority --- libs/ardour/globals.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index a017ff7d3b..80c13d96df 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -815,7 +815,8 @@ ARDOUR::init_post_engine (uint32_t start_cnt) } } - BaseUI::set_thread_priority (pbd_absolute_rt_priority (PBD_SCHED_FIFO, PBD_RT_PRI_CTRL)); + /* set/update thread priority relative to backend's [jack_]client_real_time_priority */ + BaseUI::set_thread_priority (PBD_RT_PRI_CTRL); TransportMasterManager::instance ().restart (); } From 1d921dec0bc137fe96ece1fde90941e99c79a070 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 14:47:52 +0200 Subject: [PATCH 49/75] Debug thread sched_priority --- libs/pbd/pthread_utils.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc index f798da4d8a..41a3e63a17 100644 --- a/libs/pbd/pthread_utils.cc +++ b/libs/pbd/pthread_utils.cc @@ -369,7 +369,7 @@ pbd_realtime_pthread_create ( if (stacksize > 0) { pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ()); } - DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Start Realtime Thread policy = %1 priority = %2 stacksize = 0x%3%4\n", policy, priority, std::hex, stacksize)); + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Start Realtime Thread policy = %1 priority = %2 stacksize = 0x%3%4\n", policy, parm.sched_priority, std::hex, stacksize)); rv = pthread_create (thread, &attr, start_routine, arg); pthread_attr_destroy (&attr); return rv; @@ -382,6 +382,8 @@ pbd_set_thread_priority (pthread_t thread, const int policy, int priority) memset (¶m, 0, sizeof (param)); param.sched_priority = pbd_absolute_rt_priority (policy, priority); + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Change '%1' to policy = %2 priority = %3\n", pthread_name(), policy, param.sched_priority)); + return pthread_setschedparam (thread, SCHED_FIFO, ¶m); } From 28605b53515b3d1bd8da5c1eada8946d1dffa79c Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 21:06:55 +0200 Subject: [PATCH 50/75] Fix Audio region fade property mapping This fixes an issue when undoing region fades. Particularly but not limited to the following: 1. select a region and split it in the middle 2. switch to range tool. make a range selection across the split 3. Edit > Fade > Fade range seection (or press `/`) 4. Undo Previously the fade-out of the earlier region was not undone. --- libs/ardour/audioregion.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index e813642d41..0b937f26a7 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -234,9 +234,9 @@ AudioRegion::register_properties () , _fade_before_fx (Properties::fade_before_fx, other->_fade_before_fx) \ , _scale_amplitude (Properties::scale_amplitude, other->_scale_amplitude) \ , _fade_in (Properties::fade_in, std::shared_ptr (new AutomationList (*other->_fade_in.val()))) \ - , _inverse_fade_in (Properties::fade_in, std::shared_ptr (new AutomationList (*other->_inverse_fade_in.val()))) \ - , _fade_out (Properties::fade_in, std::shared_ptr (new AutomationList (*other->_fade_out.val()))) \ - , _inverse_fade_out (Properties::fade_in, std::shared_ptr (new AutomationList (*other->_inverse_fade_out.val()))) + , _inverse_fade_in (Properties::inverse_fade_in, std::shared_ptr (new AutomationList (*other->_inverse_fade_in.val()))) \ + , _fade_out (Properties::fade_out, std::shared_ptr (new AutomationList (*other->_fade_out.val()))) \ + , _inverse_fade_out (Properties::inverse_fade_out, std::shared_ptr (new AutomationList (*other->_inverse_fade_out.val()))) /* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */ void From 6b5582deef2d7b4b60a4d08399a0d32338111e34 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 21:07:45 +0200 Subject: [PATCH 51/75] Small code cleanup and simplification --- gtk2_ardour/route_time_axis.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 0cc2043d78..062495d9ac 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -1310,7 +1310,6 @@ RouteTimeAxisView::find_next_region_boundary (timepos_t const & pos, int32_t dir void RouteTimeAxisView::fade_range (TimeSelection& selection) { - std::shared_ptr what_we_got; std::shared_ptr tr = track (); std::shared_ptr playlist; @@ -1328,11 +1327,7 @@ RouteTimeAxisView::fade_range (TimeSelection& selection) playlist->fade_range (time); - vector cmds; - playlist->rdiff (cmds); - _session->add_commands (cmds); - _session->add_command (new StatefulDiffCommand (playlist)); - + playlist->rdiff_and_add_command (_session); } void From 46f61d76626a2efbbc546ec09b6d4992925d3ca1 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 21:14:52 +0200 Subject: [PATCH 52/75] Fix and simplify Playlist::fade_range for multiple ranges --- libs/ardour/playlist.cc | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index 4b78a677d6..e4894f653c 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -3413,17 +3413,14 @@ Playlist::fade_range (list& ranges) { ThawList thawlist; RegionReadLock rlock (this); - for (list::iterator r = ranges.begin(); r != ranges.end(); ) { - list::iterator tmpr = r; - ++tmpr; - for (RegionList::const_iterator i = regions.begin (); i != regions.end ();) { - RegionList::const_iterator tmpi = i; - ++tmpi; - thawlist.add (*i); - (*i)->fade_range ((*r).start().samples(), (*r).end().samples()); - i = tmpi; + /* add regions only once, not for each range */ + for (auto const& r : regions) { + thawlist.add (r); + } + for (auto const& t: ranges) { + for (auto const& r : regions) { + r->fade_range (t.start().samples(), t.end().samples()); } - r = tmpr; } rlock.release (); thawlist.release (); From c6ef4c3545ebf829a5fdb0129c8431620bc0f112 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 28 Sep 2024 21:40:11 +0200 Subject: [PATCH 53/75] Fix interaction with snapshot list * Select snapshot before showing relevant context menu * Ignore clicks on empty space (no path) --- gtk2_ardour/editor_snapshots.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gtk2_ardour/editor_snapshots.cc b/gtk2_ardour/editor_snapshots.cc index fc6faf847a..9661916fa5 100644 --- a/gtk2_ardour/editor_snapshots.cc +++ b/gtk2_ardour/editor_snapshots.cc @@ -84,6 +84,10 @@ EditorSnapshots::button_press (GdkEventButton* ev) int cx; int cy; _snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy); + if (!path) { + return false; + } + _snapshot_display.get_selection()->select (path); Gtk::TreeModel::iterator iter = _snapshot_model->get_iter (path); if (iter) { Gtk::TreeModel::Row row = *iter; @@ -97,6 +101,9 @@ EditorSnapshots::button_press (GdkEventButton* ev) int cy; string snap_name; _snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy); + if (!path) { + return false; + } Gtk::TreeModel::iterator iter = _snapshot_model->get_iter (path); if (iter) { Gtk::TreeModel::Row row = *iter; From d60e0e7ade063e8f18a9fc5012e9eec129aa91ec Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 29 Sep 2024 18:07:59 +0200 Subject: [PATCH 54/75] Fix sample-rate display rounding for 22.05kHz Thanks to Colin Fletcher --- gtk2_ardour/utils.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc index 1bf895d938..84d2c40b0b 100644 --- a/gtk2_ardour/utils.cc +++ b/gtk2_ardour/utils.cc @@ -770,7 +770,9 @@ string ARDOUR_UI_UTILS::rate_as_string (float r) { char buf[32]; - if (fmod (r, 1000.0f)) { + if (fmod (r, 100.0f)) { + snprintf (buf, sizeof (buf), "%.2f kHz", r / 1000.0); + } else if (fmod (r, 1000.0f)) { snprintf (buf, sizeof (buf), "%.1f kHz", r / 1000.0); } else { snprintf (buf, sizeof (buf), "%.0f kHz", r / 1000.0); From 11f71a3297f8eb420fe6db7f227770edc0fcfa32 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 29 Sep 2024 18:18:26 +0200 Subject: [PATCH 55/75] Fix playback of sessions with low sample-rate previously, _chunksize = ``` minimum_disk_read_bytes / sizeof (Sample) ``` can become larger then actual allocated ringbuffer: ``` audio_playback_buffer_seconds * sample-rate ``` In which case the buffer was never filled. The disk writer has similar issues --- libs/ardour/disk_reader.cc | 7 +++++-- libs/ardour/disk_writer.cc | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/libs/ardour/disk_reader.cc b/libs/ardour/disk_reader.cc index 5210b13028..439b3c7232 100644 --- a/libs/ardour/disk_reader.cc +++ b/libs/ardour/disk_reader.cc @@ -113,8 +113,9 @@ DiskReader::ReaderChannelInfo::resize_preloop (samplecnt_t bufsize) int DiskReader::add_channel_to (std::shared_ptr c, uint32_t how_many) { + samplecnt_t bufsz = std::max (_chunk_samples * 2, _session.butler ()->audio_playback_buffer_size ()); while (how_many--) { - c->push_back (new ReaderChannelInfo (_session.butler ()->audio_playback_buffer_size (), loop_fade_length)); + c->push_back (new ReaderChannelInfo (bufsz, loop_fade_length)); DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': new reader channel, write space = %2 read = %3\n", name (), c->back ()->rbuf->write_space (), @@ -236,8 +237,10 @@ DiskReader::adjust_buffering () { std::shared_ptr c = channels.reader (); + samplecnt_t bufsz = std::max (_chunk_samples * 2, _session.butler ()->audio_playback_buffer_size ()); + for (auto const& chan : *c) { - chan->resize (_session.butler ()->audio_playback_buffer_size ()); + chan->resize (bufsz); } } diff --git a/libs/ardour/disk_writer.cc b/libs/ardour/disk_writer.cc index 9ee351e17f..6e5fb5616d 100644 --- a/libs/ardour/disk_writer.cc +++ b/libs/ardour/disk_writer.cc @@ -109,8 +109,9 @@ DiskWriter::WriterChannelInfo::resize (samplecnt_t bufsize) int DiskWriter::add_channel_to (std::shared_ptr c, uint32_t how_many) { + samplecnt_t bufsz = std::max (_chunk_samples * 2, _session.butler()->audio_capture_buffer_size()); while (how_many--) { - c->push_back (new WriterChannelInfo (_session.butler()->audio_capture_buffer_size())); + c->push_back (new WriterChannelInfo (bufsz)); DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: new writer channel, write space = %2 read = %3\n", name(), c->back()->wbuf->write_space(), @@ -1386,8 +1387,9 @@ DiskWriter::adjust_buffering () { std::shared_ptr c = channels.reader(); + samplecnt_t bufsz = std::max (_chunk_samples * 2, _session.butler()->audio_capture_buffer_size()); for (auto const chan : *c) { - chan->resize (_session.butler()->audio_capture_buffer_size()); + chan->resize (bufsz); } } From ed437afda7047a4755cdca45337719d8bd972e60 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 30 Sep 2024 16:43:03 +0200 Subject: [PATCH 56/75] Fix thread priorities for Windows builds --- libs/pbd/pbd/pthread_utils.h | 2 +- libs/pbd/pthread_utils.cc | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/libs/pbd/pbd/pthread_utils.h b/libs/pbd/pbd/pthread_utils.h index a528363230..01108861cf 100644 --- a/libs/pbd/pbd/pthread_utils.h +++ b/libs/pbd/pbd/pthread_utils.h @@ -92,7 +92,7 @@ LIBPBD_API int pbd_realtime_pthread_create ( void *arg); LIBPBD_API int pbd_absolute_rt_priority (int policy, int priority); -LIBPBD_API int pbd_set_thread_priority (pthread_t, const int policy, int priority); +LIBPBD_API int pbd_set_thread_priority (pthread_t, int policy, int priority); LIBPBD_API bool pbd_mach_set_realtime_policy (pthread_t thread_id, double period_ns, bool main); namespace PBD { diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc index 41a3e63a17..ef562a0495 100644 --- a/libs/pbd/pthread_utils.cc +++ b/libs/pbd/pthread_utils.cc @@ -1,7 +1,7 @@ /* * Copyright (C) 2002-2015 Paul Davis * Copyright (C) 2007-2009 David Robillard - * Copyright (C) 2015-2018 Robin Gareus + * Copyright (C) 2015-2024 Robin Gareus * * 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 @@ -293,17 +293,15 @@ pbd_pthread_priority (PBDThreadClass which) #ifdef PLATFORM_WINDOWS switch (which) { case THREAD_MAIN: - return -1; + return -1; // THREAD_PRIORITY_TIME_CRITICAL (15) case THREAD_MIDI: - return -2; - default: case THREAD_PROC: - return -2; + case THREAD_CTRL: + default: + return -14; // THREAD_PRIORITY_HIGHEST (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; + /* https://github.com/mingw-w64/mingw-w64/blob/master/mingw-w64-libraries/winpthreads/src/sched.c */ + return -15; // THREAD_PRIORITY_ABOVE_NORMAL (1) } #else int base = base_priority_relative_to_max; @@ -335,8 +333,8 @@ int pbd_absolute_rt_priority (int policy, int priority) { /* POSIX requires a spread of at least 32 steps between min..max */ - const int p_min = sched_get_priority_min (policy); // Linux: 1 - const int p_max = sched_get_priority_max (policy); // Linux: 99 + const int p_min = sched_get_priority_min (policy); // Linux: 1 Windows -15 + const int p_max = sched_get_priority_max (policy); // Linux: 99 Windows +15 /* priority is relative to the max */ assert (priority < 0); @@ -376,8 +374,12 @@ pbd_realtime_pthread_create ( } int -pbd_set_thread_priority (pthread_t thread, const int policy, int priority) +pbd_set_thread_priority (pthread_t thread, int policy, int priority) { +#if defined PLATFORM_WINDOWS + policy = SCHED_OTHER; +#endif + struct sched_param param; memset (¶m, 0, sizeof (param)); param.sched_priority = pbd_absolute_rt_priority (policy, priority); From dab22a7c70bdbba9c3d79d39a94d8a5681c10792 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 30 Sep 2024 19:57:53 +0200 Subject: [PATCH 57/75] Explicitly set Windows Process Scheduling Class --- libs/pbd/pthread_utils.cc | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc index ef562a0495..94cdc1da41 100644 --- a/libs/pbd/pthread_utils.cc +++ b/libs/pbd/pthread_utils.cc @@ -23,6 +23,10 @@ #include #include +#ifdef PLATFORM_WINDOWS +#include +#endif + #if !defined PLATFORM_WINDOWS && defined __GLIBC__ #include #include @@ -55,6 +59,49 @@ static pthread_mutex_t thread_map_lock = PTHREAD_MUTEX_INITIAL static Glib::Threads::Private thread_name (free); static int base_priority_relative_to_max = -20; +#ifdef PLATFORM_WINDOWS +static +std::string GetLastErrorAsString() +{ + DWORD err = ::GetLastError(); + if(err == 0) { + return std::string (); + } + + LPSTR buf = nullptr; + size_t size = FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL); + + std::string rv (buf, size); + LocalFree (buf); + return rv; +} + +static bool +set_win_set_realtime_policy (pthread_t thread, int priority) +{ + if (priority < 12) { + return false; + } + bool ok = false; + + if (SetPriorityClass (GetCurrentProcess (), 0x00000100 /* REALTIME_PRIORITY_CLASS */)) { + /* see https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities */ + ok = SetThreadPriority (pthread_gethandle (thread), priority); + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Using Windows RT thread class. set priority: %1\n", ok ? "OK" : GetLastErrorAsString ())); + } else { + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Cannot use Windows RT thread class: %1\n", GetLastErrorAsString ())); + ok = SetPriorityClass (GetCurrentProcess (), 0x00000080 /* HIGH_PRIORITY_CLASS */); + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Using Windows high priority thread class: %1\n", ok ? "OK" : GetLastErrorAsString ())); + if (ok) { + ok = SetThreadPriority (pthread_gethandle (thread), priority); + DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Set Windows high thread priority: %1\n", ok ? "OK" : GetLastErrorAsString ())); + } + } + return ok; +} +#endif + namespace PBD { PBD::Signal3 ThreadCreatedWithRequestSize; @@ -370,6 +417,12 @@ pbd_realtime_pthread_create ( DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Start Realtime Thread policy = %1 priority = %2 stacksize = 0x%3%4\n", policy, parm.sched_priority, std::hex, stacksize)); rv = pthread_create (thread, &attr, start_routine, arg); pthread_attr_destroy (&attr); + +#ifdef PLATFORM_WINDOWS + if (0 == rv && thread && parm.sched_priority >= 12) { + set_win_set_realtime_policy (*thread, parm.sched_priority); + } +#endif return rv; } @@ -386,6 +439,14 @@ pbd_set_thread_priority (pthread_t thread, int policy, int priority) DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Change '%1' to policy = %2 priority = %3\n", pthread_name(), policy, param.sched_priority)); +#ifdef PLATFORM_WINDOWS + if (thread && param.sched_priority >= 12) { + if (set_win_set_realtime_policy (thread, param.sched_priority)) { + return 0; + } + } +#endif + return pthread_setschedparam (thread, SCHED_FIFO, ¶m); } From 1aad6805b3d7c56793fe9a6d820f8717f9783683 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 30 Sep 2024 23:36:06 +0200 Subject: [PATCH 58/75] Fix count-in/preroll recording offsets Notably `Route::process_output_buffers` uses ``` output_latency = speed * _output_latency; ``` here, speed already needs to be non-zero during count-in and pre-roll. --- libs/ardour/ardour/session.h | 2 +- libs/ardour/route.cc | 8 ++++---- libs/ardour/session_transport.cc | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index bfc029ff93..e06a579c98 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -811,7 +811,7 @@ public: double engine_speed() const { return _engine_speed; } double actual_speed() const; - double transport_speed() const; + double transport_speed (bool incl_preroll = false) const; /** @return true if the transport state (TFSM) is stopped */ bool transport_stopped() const; /** @return true if the transport state (TFSM) is stopped or stopping */ diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index f5c1375362..b47cea9aa0 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -410,7 +410,7 @@ Route::process_output_buffers (BufferSet& bufs, _pannable->automation_run (start_sample, nframes); } - const int speed = (is_auditioner() ? 1 : _session.transport_speed ()); + const int speed = (is_auditioner() ? 1 : _session.transport_speed (true)); assert (speed == -1 || speed == 0 || speed == 1); const samplecnt_t output_latency = speed * _output_latency; @@ -4050,7 +4050,7 @@ Route::latency_preroll (pframes_t nframes, samplepos_t& start_sample, samplepos_ return nframes; } if (!_disk_reader) { - if (_session.transport_speed() < 0) { + if (_session.transport_speed (true) < 0) { start_sample += latency_preroll; end_sample += latency_preroll; } else { @@ -4060,12 +4060,12 @@ Route::latency_preroll (pframes_t nframes, samplepos_t& start_sample, samplepos_ return nframes; } - if (latency_preroll > playback_latency ()) { + if (latency_preroll >= playback_latency ()) { no_roll_unlocked (nframes, start_sample - latency_preroll, end_sample - latency_preroll, false); return 0; } - if (_session.transport_speed() < 0) { + if (_session.transport_speed (true) < 0) { start_sample += latency_preroll; end_sample += latency_preroll; } else { diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index c751e3258b..22fc3ee670 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -2142,11 +2142,14 @@ Session::transport_will_roll_forwards () const } double -Session::transport_speed() const +Session::transport_speed (bool incl_preroll) const { if (_transport_fsm->transport_speed() != _transport_fsm->transport_speed()) { // cerr << "\n\n!!TS " << _transport_fsm->transport_speed() << " TFSM::speed " << _transport_fsm->transport_speed() << " via " << _transport_fsm->current_state() << endl; } + if (incl_preroll) { + return _transport_fsm->transport_speed(); + } return _count_in_samples > 0 ? 0. : _transport_fsm->transport_speed(); } From 22a2cb06245b5679859e2571907a8887b12579ff Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 30 Sep 2024 23:36:36 +0200 Subject: [PATCH 59/75] Ignore inactive routes for pre-roll sub-cycles --- libs/ardour/session_process.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index 1335464731..00ecb4b87d 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -348,6 +348,9 @@ Session::calc_preroll_subcycle (samplecnt_t ns) const { std::shared_ptr r = routes.reader (); for (auto const& i : *r) { + if (!i->active ()) { + continue; + } samplecnt_t route_offset = i->playback_latency (); if (_remaining_latency_preroll > route_offset + ns) { /* route will no-roll for complete pre-roll cycle */ From ca7ac7027b65e6f610d13114ba2587a2be45edba Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 1 Oct 2024 01:21:35 +0200 Subject: [PATCH 60/75] VST3: add more debug messages for PSL extensions --- libs/ardour/vst3_plugin.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/ardour/vst3_plugin.cc b/libs/ardour/vst3_plugin.cc index 27c5f691a5..56f664d03b 100644 --- a/libs/ardour/vst3_plugin.cc +++ b/libs/ardour/vst3_plugin.cc @@ -3030,7 +3030,7 @@ VST3PI::setContextInfoValue (FIDString id, double value) return kInvalidArgument; // send index out of bounds } } else { - DEBUG_TRACE (DEBUG::VST3Callbacks, "VST3PI::setContextInfoValue: unsupported ID\n"); + DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::setContextInfoValue: unsupported ID %1\n", id)); return kInvalidArgument; } return kResultOk; @@ -3071,7 +3071,7 @@ VST3PI::setContextInfoValue (FIDString id, int32 value) s->session ().set_control (ac, value != 0 ? 1 : 0, Controllable::NoGroup); } } else { - DEBUG_TRACE (DEBUG::VST3Callbacks, "VST3PI::setContextInfoValue: unsupported ID\n"); + DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::setContextInfoValue: unsupported ID %1\n", id)); return kNotImplemented; } return kResultOk; @@ -3101,6 +3101,7 @@ VST3PI::beginEditContextInfoValue (FIDString id) } std::shared_ptr ac = lookup_ac (_owner, id); if (!ac) { + DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::beginEditContextInfoValue %1 -- invalid AC\n", id)); return kInvalidArgument; } DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::beginEditContextInfoValue %1\n", id)); @@ -3117,6 +3118,7 @@ VST3PI::endEditContextInfoValue (FIDString id) } std::shared_ptr ac = lookup_ac (_owner, id); if (!ac) { + DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::endEditContextInfoValue %1 -- invalid AC\n", id)); return kInvalidArgument; } DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::endEditContextInfoValue %1\n", id)); From 4fc4a2ca09ba067b057aa6dc0367ff24c343bf2a Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 1 Oct 2024 05:43:12 +0200 Subject: [PATCH 61/75] Pick a sane default for "insert time" --- gtk2_ardour/insert_remove_time_dialog.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk2_ardour/insert_remove_time_dialog.cc b/gtk2_ardour/insert_remove_time_dialog.cc index 4723b6a0cc..9f637c80a3 100644 --- a/gtk2_ardour/insert_remove_time_dialog.cc +++ b/gtk2_ardour/insert_remove_time_dialog.cc @@ -89,7 +89,7 @@ InsertRemoveTimeDialog::InsertRemoveTimeDialog (PublicEditor& e, bool remove) _intersected_combo.append (_("stay in position")); _intersected_combo.append (_("move")); _intersected_combo.append (_("be split")); - _intersected_combo.set_active (0); + _intersected_combo.set_active (2); table->attach (_intersected_combo, 1, 2, 2, 3); } From 44b2377e72c2cf42cb5743e1778d3e91fe1dd260 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 1 Oct 2024 19:24:36 +0200 Subject: [PATCH 62/75] VST3: Fix possible deadlock when using PSL extension for sends see also c5618f01d61 --- libs/ardour/vst3_plugin.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/ardour/vst3_plugin.cc b/libs/ardour/vst3_plugin.cc index 56f664d03b..fedd9141de 100644 --- a/libs/ardour/vst3_plugin.cc +++ b/libs/ardour/vst3_plugin.cc @@ -2964,12 +2964,12 @@ VST3PI::getContextInfoValue (double& value, FIDString id) if (0 == strcmp (id, ContextInfo::kMaxVolume)) { value = s->gain_control ()->upper (); } else if (0 == strcmp (id, ContextInfo::kMaxSendLevel)) { + value = 2.0; // Config->get_max_gain(); #ifdef MIXBUS - if (s->send_level_controllable (0)) { + if (s->send_enable_controllable (0)) { value = s->send_level_controllable (0)->upper (); // pow (10.0, .05 * 15.0); } #endif - value = 2.0; // Config->get_max_gain(); } else if (0 == strcmp (id, ContextInfo::kVolume)) { std::shared_ptr ac = s->gain_control (); value = ac->get_value (); // gain coefficient 0..2 (1.0 = 0dB) From 07c79ce92cd82523973ff8d2324ec87536615d43 Mon Sep 17 00:00:00 2001 From: John Emmas Date: Tue, 1 Oct 2024 20:28:16 +0100 Subject: [PATCH 63/75] Small changes to make pthread_utils.cc buildable again with MSVC --- libs/pbd/pthread_utils.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/pbd/pthread_utils.cc b/libs/pbd/pthread_utils.cc index 94cdc1da41..88e21d2624 100644 --- a/libs/pbd/pthread_utils.cc +++ b/libs/pbd/pthread_utils.cc @@ -43,6 +43,8 @@ DECLARE_DEFAULT_COMPARISONS (pthread_t) // Needed for 'DECLARE_DEFAULT_COMPARISO // if the type of object being contained has no appropriate comparison operators // defined (specifically, if operators '<' and '==' are undefined). This seems // to be the case with ptw32 'pthread_t' which is a simple struct. + +#define pthread_gethandle pthread_getw32threadhandle_np #endif #ifdef __APPLE__ @@ -440,7 +442,7 @@ pbd_set_thread_priority (pthread_t thread, int policy, int priority) DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Change '%1' to policy = %2 priority = %3\n", pthread_name(), policy, param.sched_priority)); #ifdef PLATFORM_WINDOWS - if (thread && param.sched_priority >= 12) { + if (is_pthread_active (thread) && param.sched_priority >= 12) { if (set_win_set_realtime_policy (thread, param.sched_priority)) { return 0; } From adc9d9e0af62e4329f0a641ef50e23721d74e971 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 1 Oct 2024 22:58:44 +0200 Subject: [PATCH 64/75] VST3: work around plugins that do not heed ContextInfo::kSendCount see also c5618f01d61 --- libs/ardour/ardour/route.h | 2 +- libs/ardour/ardour/stripable.h | 2 +- libs/ardour/ardour/vca.h | 2 +- libs/ardour/ardour/vst3_plugin.h | 4 ++++ libs/ardour/route.cc | 9 ++++++++- libs/ardour/vst3_plugin.cc | 17 ++++++++++++----- 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index c157c46439..2f270eb7d9 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -553,7 +553,7 @@ public: std::shared_ptr mapped_control (enum WellKnownCtrl, uint32_t band = 0) const; std::shared_ptr mapped_output (enum WellKnownData) const; - std::shared_ptr send_level_controllable (uint32_t n) const; + std::shared_ptr send_level_controllable (uint32_t n, bool locked = false) const; std::shared_ptr send_enable_controllable (uint32_t n) const; std::shared_ptr send_pan_azimuth_controllable (uint32_t n) const; std::shared_ptr send_pan_azimuth_enable_controllable (uint32_t n) const; diff --git a/libs/ardour/ardour/stripable.h b/libs/ardour/ardour/stripable.h index c21e9cc7f2..0af0f48ef5 100644 --- a/libs/ardour/ardour/stripable.h +++ b/libs/ardour/ardour/stripable.h @@ -158,7 +158,7 @@ class LIBARDOUR_API Stripable : public SessionObject, * In Ardour, these are user-created sends that connect to user-created * Aux busses. */ - virtual std::shared_ptr send_level_controllable (uint32_t n) const = 0; + virtual std::shared_ptr send_level_controllable (uint32_t n, bool locked = false) const = 0; virtual std::shared_ptr send_enable_controllable (uint32_t n) const = 0; virtual std::shared_ptr send_pan_azimuth_controllable (uint32_t n) const = 0; virtual std::shared_ptr send_pan_azimuth_enable_controllable (uint32_t n) const = 0; diff --git a/libs/ardour/ardour/vca.h b/libs/ardour/ardour/vca.h index 76885d2c8d..b660e6ffb8 100644 --- a/libs/ardour/ardour/vca.h +++ b/libs/ardour/ardour/vca.h @@ -134,7 +134,7 @@ class LIBARDOUR_API VCA : public Stripable, uint32_t eq_band_cnt () const { return 0; } std::string eq_band_name (uint32_t) const { return std::string(); } - std::shared_ptr send_level_controllable (uint32_t n) const { return std::shared_ptr(); } + std::shared_ptr send_level_controllable (uint32_t n, bool locked = false) const { return std::shared_ptr(); } std::shared_ptr send_enable_controllable (uint32_t n) const { return std::shared_ptr(); } std::shared_ptr send_pan_azimuth_controllable (uint32_t n) const { return std::shared_ptr(); } std::shared_ptr send_pan_azimuth_enable_controllable (uint32_t n) const { return std::shared_ptr(); } diff --git a/libs/ardour/ardour/vst3_plugin.h b/libs/ardour/ardour/vst3_plugin.h index 3ac40037b6..8a9d83fb1a 100644 --- a/libs/ardour/ardour/vst3_plugin.h +++ b/libs/ardour/ardour/vst3_plugin.h @@ -359,6 +359,10 @@ private: bool _no_kMono; /* work around yabridge threading */ bool _restart_component_is_synced; + /* work around PSL calls during set_owner, + * while the route holds a processor lock + */ + std::atomic _in_set_owner; }; } // namespace Steinberg diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index b47cea9aa0..2c0e187118 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -5927,8 +5927,15 @@ Route::send_pan_azimuth_controllable (uint32_t n) const } std::shared_ptr -Route::send_level_controllable (uint32_t n) const +Route::send_level_controllable (uint32_t n, bool locked) const { + if (locked) { + /* calling thread has a WriterLock (_processor_lock) + * we cannot call nth_send() + */ + return std::shared_ptr(); + } + std::shared_ptr s = std::dynamic_pointer_cast(nth_send (n)); if (s) { return s->gain_control (); diff --git a/libs/ardour/vst3_plugin.cc b/libs/ardour/vst3_plugin.cc index fedd9141de..10cb6a42f2 100644 --- a/libs/ardour/vst3_plugin.cc +++ b/libs/ardour/vst3_plugin.cc @@ -1195,6 +1195,7 @@ VST3PI::VST3PI (std::shared_ptr m, std::string unique_ , _rpc_queue (RouteProcessorChange::NoProcessorChange, false) , _no_kMono (false) , _restart_component_is_synced (false) + , _in_set_owner (false) { using namespace std; IPluginFactory* factory = m->factory (); @@ -1835,9 +1836,13 @@ VST3PI::set_owner (SessionObject* o) return; } + _in_set_owner.store (true); + if (!setup_psl_info_handler ()) { setup_info_listener (); } + + _in_set_owner.store (false); } void @@ -2809,7 +2814,7 @@ VST3PI::automation_state_changed (uint32_t port, AutoState s, std::weak_ptr -lookup_ac (SessionObject* o, FIDString id) +lookup_ac (SessionObject* o, FIDString id, bool locked = false) { Stripable* s = dynamic_cast (o); if (!s) { @@ -2842,8 +2847,8 @@ lookup_ac (SessionObject* o, FIDString id) * recurive locks (deadlock, or double unlock crash). */ int send_id = atoi (id + strlen (ContextInfo::kSendLevel)); - if (s->send_enable_controllable (send_id)) { - return s->send_level_controllable (send_id); + if (send_id >=0 && s->send_enable_controllable (send_id)) { + return s->send_level_controllable (send_id, locked); } #endif } @@ -2967,6 +2972,7 @@ VST3PI::getContextInfoValue (double& value, FIDString id) value = 2.0; // Config->get_max_gain(); #ifdef MIXBUS if (s->send_enable_controllable (0)) { + assert (s->send_level_controllable (0)); value = s->send_level_controllable (0)->upper (); // pow (10.0, .05 * 15.0); } #endif @@ -2983,11 +2989,12 @@ VST3PI::getContextInfoValue (double& value, FIDString id) value = 0.5; // center } } else if (0 == strncmp (id, ContextInfo::kSendLevel, strlen (ContextInfo::kSendLevel))) { - std::shared_ptr ac = lookup_ac (_owner, id); + std::shared_ptr ac = lookup_ac (_owner, id, _in_set_owner.load ()); if (ac) { value = ac->get_value (); // gain cofficient psl_subscribe_to (ac, id); } else { + value = 0; DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::getContextInfoValue invalid AC %1\n", id)); return kInvalidArgument; // send index out of bounds } @@ -3023,7 +3030,7 @@ VST3PI::setContextInfoValue (FIDString id, double value) ac->set_value (ac->interface_to_internal (value, true), PBD::Controllable::NoGroup); } } else if (0 == strncmp (id, ContextInfo::kSendLevel, strlen (ContextInfo::kSendLevel))) { - std::shared_ptr ac = lookup_ac (_owner, id); + std::shared_ptr ac = lookup_ac (_owner, id, _in_set_owner.load ()); if (ac) { ac->set_value (value, Controllable::NoGroup); } else { From 52336eb2f07b5bbf4091d93ccc6da405c76223d7 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 2 Oct 2024 09:13:28 -0600 Subject: [PATCH 65/75] fix MIDI Clock output times by using the correct call to get tempo at a position Could be worth a double check of the codebase/hiding the API that ignores ramping --- libs/ardour/ticker.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/ardour/ticker.cc b/libs/ardour/ticker.cc index 73191da257..5cd852ce55 100644 --- a/libs/ardour/ticker.cc +++ b/libs/ardour/ticker.cc @@ -218,8 +218,8 @@ out: double MidiClockTicker::one_ppqn_in_samples (samplepos_t transport_position) const { - Tempo const & tempo (TempoMap::use()->metric_at (timepos_t (transport_position)).tempo()); - const double samples_per_quarter_note = tempo.samples_per_quarter_note (_session.nominal_sample_rate()); + TempoPoint const & tempo (TempoMap::use()->metric_at (timepos_t (transport_position)).tempo()); + const double samples_per_quarter_note = superclock_to_samples (tempo.superclocks_per_note_type_at (timepos_t (transport_position)), _session.nominal_sample_rate()); return samples_per_quarter_note / 24.0; } From e3e014bfe6bbf1d2feb45784f507fff52671b2cc Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 2 Oct 2024 11:03:08 -0600 Subject: [PATCH 66/75] NO-OP: comment work --- libs/temporal/temporal/tempo.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libs/temporal/temporal/tempo.h b/libs/temporal/temporal/tempo.h index b131686032..549749fc82 100644 --- a/libs/temporal/temporal/tempo.h +++ b/libs/temporal/temporal/tempo.h @@ -263,7 +263,9 @@ class LIBTEMPORAL_API Tempo { superclock_t _end_superclocks_per_note_type; int8_t _note_type; bool _locked_to_meter; /* XXX name has unclear meaning with nutempo */ - bool _continuing; + bool _continuing; /* true if our effective end tempo is defined + * by the following tempo in the TempoMap; + * false if we use our own end tempo. */ static inline superclock_t double_npm_to_scpn (double npm) { return (superclock_t) llround ((60./npm) * superclock_ticks_per_second()); } @@ -348,8 +350,8 @@ class /*LIBTEMPORAL_API*/ MeterPoint : public Meter, public meter_hook, public v }; /* A TempoPoint is a combination of a Tempo with a Point. However, if the temp - * is ramped, then at some point we will need to compute the ramp coefficients - * (c-per-quarter and c-per-superclock) and store them so that we can compute + * is ramped, then at some point we will need to compute the ramp coefficient + * (_omega) and store it so that we can compute tempo-at-time and * time-at-quarter-note on demand. */ From 33806a273504888d513b44ec596904fbfd59e50b Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 3 Oct 2024 17:28:09 +0200 Subject: [PATCH 67/75] Revert "Only enable RegionFX in debug builds for the time being" This reverts commit 2d076cccb1286b0ea662009d9147dc06592ba643. --- gtk2_ardour/audio_region_editor.cc | 2 -- gtk2_ardour/region_editor.cc | 4 ---- libs/ardour/luabindings.cc | 2 -- 3 files changed, 8 deletions(-) diff --git a/gtk2_ardour/audio_region_editor.cc b/gtk2_ardour/audio_region_editor.cc index 3f3d487876..d8bb8e8af8 100644 --- a/gtk2_ardour/audio_region_editor.cc +++ b/gtk2_ardour/audio_region_editor.cc @@ -98,7 +98,6 @@ AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* arv) _table.attach (_fade_before_fx_toggle, 2, 3, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL); ++_table_row; -#ifndef NDEBUG // disable region Fx for now _region_line_label.set_name ("AudioRegionEditorLabel"); _region_line_label.set_text (_("Region Line:")); _region_line_label.set_alignment (1, 0.5); @@ -106,7 +105,6 @@ AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* arv) _table.attach (_region_line, 1, 2, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL); _table.attach (_show_on_touch, 2, 3, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL); ++_table_row; -#endif UI::instance()->set_tip (_polarity_toggle, _("Invert the signal polarity (180deg phase shift)")); UI::instance()->set_tip (_fade_before_fx_toggle, _("Apply region effects after the region fade.\nThis is useful if the effect(s) have tail, which would otherwise be faded out by the region fade (e.g. reverb, delay)")); diff --git a/gtk2_ardour/region_editor.cc b/gtk2_ardour/region_editor.cc index a12e2cdd69..af85c7f562 100644 --- a/gtk2_ardour/region_editor.cc +++ b/gtk2_ardour/region_editor.cc @@ -187,10 +187,8 @@ RegionEditor::RegionEditor (Session* s, RegionView* rv) _table.attach (_sources, 1, 2, _table_row, _table_row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL); ++_table_row; -#ifndef NDEBUG // disable region FX for now _table.attach (region_fx_label, 2, 3, 0, 1, Gtk::FILL, Gtk::FILL); _table.attach (_region_fx_box, 2, 3, 1, _table_row + 2, Gtk::FILL, Gtk::FILL); -#endif get_vbox()->pack_start (_table, true, true); @@ -232,13 +230,11 @@ RegionEditor::RegionEditor (Session* s, RegionView* rv) spin_arrow_grab = false; -#ifndef NDEBUG // disable region FX for now /* for now only audio region effects are supported */ if (std::dynamic_pointer_cast (_region)) { region_fx_label.show (); _region_fx_box.show (); } -#endif connect_editor_events (); } diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index 5a6dc3b772..5595f018d8 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -1654,12 +1654,10 @@ LuaBindings::common (lua_State* L) .addFunction ("has_transients", &Region::has_transients) .addFunction ("transients", (AnalysisFeatureList (Region::*)())&Region::transients) -#ifndef NDEBUG // disable region FX for now .addFunction ("load_plugin", &Region::load_plugin) .addFunction ("add_plugin", &Region::add_plugin) .addFunction ("remove_plugin", &Region::add_plugin) .addFunction ("nth_plugin", &Region::nth_plugin) -#endif /* editing operations */ .addFunction ("set_length", &Region::set_length) From 59e98bd824deee283d1e8089c2fc0880b645d6b4 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 8 Oct 2024 11:54:14 +0200 Subject: [PATCH 68/75] Fix reading peak-file after EOF This issue was introduced in b28090c64c8ef --- libs/ardour/audiosource.cc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index 823d350bcc..20f9eeaabf 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -575,8 +575,6 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos * to avoid confusion, I'll refer to the requested peaks as visual_peaks and the peakfile peaks as stored_peaks */ - const samplecnt_t chunksize = (samplecnt_t) expected_peaks; // we read all the peaks we need in one hit. - /* compute the rounded up sample position */ samplepos_t next_visual_peak = (samplepos_t) ceil (start / samples_per_visual_peak); @@ -589,11 +587,18 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos /* open ... close during out: handling */ - off_t map_off = (uint32_t) (current_stored_peak) * sizeof(PeakData); + off_t map_off = (uint32_t) (current_stored_peak) * sizeof(PeakData); off_t read_map_off = map_off & ~(bufsize - 1); - off_t map_delta = map_off - read_map_off; + off_t map_delta = map_off - read_map_off; + + samplecnt_t max_chunk = (statbuf.st_size - read_map_off - map_delta) / sizeof(PeakData); + samplecnt_t chunksize = std::min (expected_peaks, max_chunk); + size_t raw_map_length = chunksize * sizeof(PeakData); - size_t map_length = (chunksize * sizeof(PeakData)) + map_delta; + size_t map_length = raw_map_length + map_delta; + + assert (read_map_off + (off_t)map_length <= statbuf.st_size); + assert (read_map_off + map_delta + (off_t)raw_map_length <= statbuf.st_size); if (_first_run || (_last_scale != samples_per_visual_peak) || (_last_map_off != map_off) || (_last_raw_map_length < raw_map_length)) { From 902ecef7970b8ce4fb7c0386c3836af254a58fa1 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 8 Oct 2024 12:50:19 +0200 Subject: [PATCH 69/75] Fix typo --- gtk2_ardour/mixer_ui.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk2_ardour/mixer_ui.cc b/gtk2_ardour/mixer_ui.cc index 91c37f88c2..5e646ce867 100644 --- a/gtk2_ardour/mixer_ui.cc +++ b/gtk2_ardour/mixer_ui.cc @@ -4411,7 +4411,7 @@ Mixer_UI::toggle_surround_master () if (want_sm) { _session->config.set_use_surround_master (true); } else { - ArdourMessageDialog md (_("Disabling surround master will delete all existing surround panner state.\nThis cannot be undonoe. Proceed anyway?"), false, MESSAGE_QUESTION, BUTTONS_YES_NO); + ArdourMessageDialog md (_("Disabling surround master will delete all existing surround panner state.\nThis cannot be undone. Proceed anyway?"), false, MESSAGE_QUESTION, BUTTONS_YES_NO); if (md.run () == RESPONSE_YES) { _session->config.set_use_surround_master (false); } From 4b83915335781d51186f9deb6ac5c1070d85ba81 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 8 Oct 2024 13:03:49 +0200 Subject: [PATCH 70/75] Prefer bundled python for windows/gdb --- tools/x-win/package.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/x-win/package.sh b/tools/x-win/package.sh index 3b192d722c..06b59459c1 100755 --- a/tools/x-win/package.sh +++ b/tools/x-win/package.sh @@ -313,6 +313,7 @@ if test -n "$PACKAGE_GDB"; then cp -r ${SRCCACHE}/gdb12 $DESTDIR/gdb12 cat > $DESTDIR/debug.bat << EOF set PYTHONPATH=%~dp0\gdb12\python3.10 +set PATH=%~dp0\gdb12\;%PATH% cd bin ..\\gdb12\\gdb.exe -ex "set logging overwrite on" -ex "set height 0" -ex "set logging file %UserProfile%\\${PRODUCT_NAME}-debug.log" -ex "set logging enabled on" -ex "target exec ${PRODUCT_EXE}" -ex "run" EOF From cae710cd7c47beda536ac95f65cde20e5e4e23f5 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 8 Oct 2024 16:23:19 +0200 Subject: [PATCH 71/75] Only enable RegionFX in debug builds for the time being --- gtk2_ardour/audio_region_editor.cc | 2 ++ gtk2_ardour/region_editor.cc | 4 ++++ libs/ardour/luabindings.cc | 2 ++ 3 files changed, 8 insertions(+) diff --git a/gtk2_ardour/audio_region_editor.cc b/gtk2_ardour/audio_region_editor.cc index d8bb8e8af8..3f3d487876 100644 --- a/gtk2_ardour/audio_region_editor.cc +++ b/gtk2_ardour/audio_region_editor.cc @@ -98,6 +98,7 @@ AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* arv) _table.attach (_fade_before_fx_toggle, 2, 3, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL); ++_table_row; +#ifndef NDEBUG // disable region Fx for now _region_line_label.set_name ("AudioRegionEditorLabel"); _region_line_label.set_text (_("Region Line:")); _region_line_label.set_alignment (1, 0.5); @@ -105,6 +106,7 @@ AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* arv) _table.attach (_region_line, 1, 2, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL); _table.attach (_show_on_touch, 2, 3, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL); ++_table_row; +#endif UI::instance()->set_tip (_polarity_toggle, _("Invert the signal polarity (180deg phase shift)")); UI::instance()->set_tip (_fade_before_fx_toggle, _("Apply region effects after the region fade.\nThis is useful if the effect(s) have tail, which would otherwise be faded out by the region fade (e.g. reverb, delay)")); diff --git a/gtk2_ardour/region_editor.cc b/gtk2_ardour/region_editor.cc index af85c7f562..a12e2cdd69 100644 --- a/gtk2_ardour/region_editor.cc +++ b/gtk2_ardour/region_editor.cc @@ -187,8 +187,10 @@ RegionEditor::RegionEditor (Session* s, RegionView* rv) _table.attach (_sources, 1, 2, _table_row, _table_row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL); ++_table_row; +#ifndef NDEBUG // disable region FX for now _table.attach (region_fx_label, 2, 3, 0, 1, Gtk::FILL, Gtk::FILL); _table.attach (_region_fx_box, 2, 3, 1, _table_row + 2, Gtk::FILL, Gtk::FILL); +#endif get_vbox()->pack_start (_table, true, true); @@ -230,11 +232,13 @@ RegionEditor::RegionEditor (Session* s, RegionView* rv) spin_arrow_grab = false; +#ifndef NDEBUG // disable region FX for now /* for now only audio region effects are supported */ if (std::dynamic_pointer_cast (_region)) { region_fx_label.show (); _region_fx_box.show (); } +#endif connect_editor_events (); } diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index 5595f018d8..5a6dc3b772 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -1654,10 +1654,12 @@ LuaBindings::common (lua_State* L) .addFunction ("has_transients", &Region::has_transients) .addFunction ("transients", (AnalysisFeatureList (Region::*)())&Region::transients) +#ifndef NDEBUG // disable region FX for now .addFunction ("load_plugin", &Region::load_plugin) .addFunction ("add_plugin", &Region::add_plugin) .addFunction ("remove_plugin", &Region::add_plugin) .addFunction ("nth_plugin", &Region::nth_plugin) +#endif /* editing operations */ .addFunction ("set_length", &Region::set_length) From 2a96c9ce98d10eac2c660187826666f6b997502a Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 8 Oct 2024 08:10:41 -0600 Subject: [PATCH 72/75] fix issues with MIDI playback (and who knows what else) near start of roll --- libs/ardour/route.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 2c0e187118..9c7056bd03 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -4060,7 +4060,7 @@ Route::latency_preroll (pframes_t nframes, samplepos_t& start_sample, samplepos_ return nframes; } - if (latency_preroll >= playback_latency ()) { + if (latency_preroll > playback_latency ()) { no_roll_unlocked (nframes, start_sample - latency_preroll, end_sample - latency_preroll, false); return 0; } From 14ff2f2e68e8a1e45ba80b16674edccffc6438c2 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Wed, 9 Oct 2024 20:49:36 +0200 Subject: [PATCH 73/75] Revert "Only enable RegionFX in debug builds for the time being" This reverts commit cae710cd7c47beda536ac95f65cde20e5e4e23f5. --- gtk2_ardour/audio_region_editor.cc | 2 -- gtk2_ardour/region_editor.cc | 4 ---- libs/ardour/luabindings.cc | 2 -- 3 files changed, 8 deletions(-) diff --git a/gtk2_ardour/audio_region_editor.cc b/gtk2_ardour/audio_region_editor.cc index 3f3d487876..d8bb8e8af8 100644 --- a/gtk2_ardour/audio_region_editor.cc +++ b/gtk2_ardour/audio_region_editor.cc @@ -98,7 +98,6 @@ AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* arv) _table.attach (_fade_before_fx_toggle, 2, 3, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL); ++_table_row; -#ifndef NDEBUG // disable region Fx for now _region_line_label.set_name ("AudioRegionEditorLabel"); _region_line_label.set_text (_("Region Line:")); _region_line_label.set_alignment (1, 0.5); @@ -106,7 +105,6 @@ AudioRegionEditor::AudioRegionEditor (Session* s, AudioRegionView* arv) _table.attach (_region_line, 1, 2, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL); _table.attach (_show_on_touch, 2, 3, _table_row, _table_row + 1, Gtk::FILL, Gtk::FILL); ++_table_row; -#endif UI::instance()->set_tip (_polarity_toggle, _("Invert the signal polarity (180deg phase shift)")); UI::instance()->set_tip (_fade_before_fx_toggle, _("Apply region effects after the region fade.\nThis is useful if the effect(s) have tail, which would otherwise be faded out by the region fade (e.g. reverb, delay)")); diff --git a/gtk2_ardour/region_editor.cc b/gtk2_ardour/region_editor.cc index a12e2cdd69..af85c7f562 100644 --- a/gtk2_ardour/region_editor.cc +++ b/gtk2_ardour/region_editor.cc @@ -187,10 +187,8 @@ RegionEditor::RegionEditor (Session* s, RegionView* rv) _table.attach (_sources, 1, 2, _table_row, _table_row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL); ++_table_row; -#ifndef NDEBUG // disable region FX for now _table.attach (region_fx_label, 2, 3, 0, 1, Gtk::FILL, Gtk::FILL); _table.attach (_region_fx_box, 2, 3, 1, _table_row + 2, Gtk::FILL, Gtk::FILL); -#endif get_vbox()->pack_start (_table, true, true); @@ -232,13 +230,11 @@ RegionEditor::RegionEditor (Session* s, RegionView* rv) spin_arrow_grab = false; -#ifndef NDEBUG // disable region FX for now /* for now only audio region effects are supported */ if (std::dynamic_pointer_cast (_region)) { region_fx_label.show (); _region_fx_box.show (); } -#endif connect_editor_events (); } diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index 5a6dc3b772..5595f018d8 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -1654,12 +1654,10 @@ LuaBindings::common (lua_State* L) .addFunction ("has_transients", &Region::has_transients) .addFunction ("transients", (AnalysisFeatureList (Region::*)())&Region::transients) -#ifndef NDEBUG // disable region FX for now .addFunction ("load_plugin", &Region::load_plugin) .addFunction ("add_plugin", &Region::add_plugin) .addFunction ("remove_plugin", &Region::add_plugin) .addFunction ("nth_plugin", &Region::nth_plugin) -#endif /* editing operations */ .addFunction ("set_length", &Region::set_length) From 43dd75b10ef37a68a02e90bd693f03e4b0abcbb1 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 9 Oct 2024 16:53:45 -0600 Subject: [PATCH 74/75] invalidate audio region cache when the region _start changes --- libs/ardour/audioregion.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 0b937f26a7..361974a0e8 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -272,6 +272,7 @@ AudioRegion::send_change (const PropertyChange& what_changed) our_interests.add (Properties::envelope); our_interests.add (Properties::fade_in); our_interests.add (Properties::fade_out); + our_interests.add (Properties::start); if (what_changed.contains (our_interests)) { _invalidated.exchange (true); From 5efa5b45298362804b999fc1a8fa0837ee91a200 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 10 Oct 2024 02:45:03 +0200 Subject: [PATCH 75/75] Optimize audio region read when no regionFX are used --- libs/ardour/audioregion.cc | 51 ++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 361974a0e8..4a7e74432e 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -734,7 +734,8 @@ AudioRegion::read_at (Sample* buf, boost::scoped_array gain_array; boost::scoped_array mixdown_array; - // TODO optimize mono reader, w/o plugins -> old code + bool nofx = false; // apply region fades at the end + if (n_chn > 1 && _cache_start < _cache_end && internal_offset + suffix >= _cache_start && internal_offset + suffix + can_read <= _cache_end) { DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region '%1' channel: %2 copy from cache %3 - %4 to_read: %5 can_read: %6\n", name(), chan_n, internal_offset + suffix, internal_offset + suffix + can_read, to_read, can_read)); @@ -747,11 +748,39 @@ AudioRegion::read_at (Sample* buf, lm.release (); samplecnt_t n_read = to_read; //< data to read from disk - samplecnt_t n_proc = to_read; //< silence pad data to process - samplepos_t n_tail = 0; // further silence pad, read tail from FX - samplepos_t readat = pos; sampleoffset_t offset = internal_offset; + /* don't use cache when there are no region FX */ + if (!have_fx) { + cl.release (); + if (read_from_sources (_sources, lsamples, mixdown_buffer, pos, n_read, chan_n) != to_read) { + return 0; + } + + /* APPLY REGULAR GAIN CURVES AND SCALING TO mixdown_buffer */ + if (envelope_active()) { + _envelope->curve().get_vector (timepos_t (offset), timepos_t (offset + n_read), gain_buffer, n_read); + + if (_scale_amplitude != 1.0f) { + for (samplecnt_t n = 0; n < n_read; ++n) { + mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude; + } + } else { + for (samplecnt_t n = 0; n < n_read; ++n) { + mixdown_buffer[n] *= gain_buffer[n]; + } + } + } else if (_scale_amplitude != 1.0f) { + apply_gain_to_buffer (mixdown_buffer, n_read, _scale_amplitude); + } + nofx = true; + goto endread; + } + + samplecnt_t n_proc = to_read; //< silence pad data to process + samplepos_t n_tail = 0; // further silence pad, read tail from FX + samplepos_t readat = pos; + if (tsamples > 0 && cnt >= esamples) { n_tail = can_read - n_read; n_proc += n_tail; @@ -800,7 +829,7 @@ AudioRegion::read_at (Sample* buf, */ if (read_from_sources (_sources, lsamples, mixdown_buffer, readat, n_read, chn) != n_read) { - return 0; // XXX + return 0; } /* APPLY REGULAR GAIN CURVES AND SCALING TO mixdown_buffer */ @@ -908,6 +937,7 @@ AudioRegion::read_at (Sample* buf, _cache_tail = n_tail; cl.release (); } +endread: /* APPLY FADES TO THE DATA IN mixdown_buffer AND MIX THE RESULTS INTO * buf. The key things to realize here: (1) the fade being applied is @@ -954,7 +984,7 @@ AudioRegion::read_at (Sample* buf, _fade_in->curve().get_vector (timepos_t (internal_offset), timepos_t (internal_offset + fade_in_limit), gain_buffer, fade_in_limit); } - if (!_fade_before_fx) { + if (!_fade_before_fx || nofx) { /* Mix our newly-read data in, with the fade */ for (samplecnt_t n = 0; n < fade_in_limit; ++n) { buf[n] += mixdown_buffer[n] * gain_buffer[n]; @@ -999,7 +1029,7 @@ AudioRegion::read_at (Sample* buf, _fade_out->curve().get_vector (timepos_t (curve_offset), timepos_t (curve_offset + fade_out_limit), gain_buffer, fade_out_limit); } - if (!_fade_before_fx) { + if (!_fade_before_fx || nofx) { /* Mix our newly-read data with whatever was already there, with the fade out applied to our data. */ for (samplecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { buf[m] += mixdown_buffer[m] * gain_buffer[n]; @@ -2542,6 +2572,13 @@ AudioRegion::remove_plugin (std::shared_ptr fx) } _plugins.erase (i); + if (_plugins.empty ()) { + Glib::Threads::Mutex::Lock cl (_cache_lock); + _cache_start = _cache_end = -1; + _cache_tail = 0; + _readcache.clear (); + } + lm.release (); fx->drop_references ();