diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index cdb4848..de2c60c 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -2,26 +2,15 @@ cmake_minimum_required(VERSION 3.5) project(core) set(CMAKE_CXX_STANDARD 23) -include(FetchContent) -FetchContent_Declare( - tracy - GIT_REPOSITORY https://github.com/wolfpld/tracy.git - GIT_TAG master - GIT_SHALLOW TRUE - GIT_PROGRESS TRUE -) -FetchContent_MakeAvailable(tracy) - set(ALL_FILES "") retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} ALL_FILES) add_library(${PROJECT_NAME} SHARED ${ALL_FILES}) - target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(${PROJECT_NAME} PUBLIC rtaudio spdlog mempool Taskflow glfw Tracy::TracyClient) +target_link_libraries(${PROJECT_NAME} PUBLIC rtaudio spdlog mempool Taskflow glfw) add_definitions(-Dcore_EXPORTS) @@ -42,8 +31,3 @@ elseif(APPLE) elseif(UNIX AND NOT APPLE) target_compile_definitions(${PROJECT_NAME} PUBLIC PLATFORM_WINDOWS=0 PLATFORM_MACOS=0 PLATFORM_LINUX=1 GLFW_EXPOSE_NATIVE_X11) endif() - -# is debug -if (CMAKE_BUILD_TYPE MATCHES Debug) - add_compile_definitions(-DTRACY_ENABLE=1) -endif () diff --git a/core/audio/device/audio_device_manager.cpp b/core/audio/device/audio_device_manager.cpp index 3f38819..32b41e4 100644 --- a/core/audio/device/audio_device_manager.cpp +++ b/core/audio/device/audio_device_manager.cpp @@ -6,6 +6,7 @@ #include "audio/plugin_host/plugin_host_manager.h" #include "spdlog/spdlog.h" #include "thread_message/thread_message_hubs.h" +#include "misc/platform_thread.h" IMPL_SINGLETON_INSTANCE(audio_device_manager) @@ -32,9 +33,9 @@ void audio_device_manager::init(singleton_initliazer& initliazer) { void audio_device_manager::release(singleton_release_guard& release_guard) { singleton_t::release(release_guard); + stop_render_thread(); release_guard.require_release(); audio_->stopStream(); - stop_render_thread(); delete audio_; } @@ -116,18 +117,27 @@ void audio_device_manager::log_current_device_info() { } void audio_device_manager::start_render_thread() { + render_thread_should_stop_ = false; render_thread_ = std::thread(&audio_device_manager::render_thread, this); render_thread_.detach(); + spdlog::info("port_audio render thread started"); } void audio_device_manager::stop_render_thread() { + render_thread_should_stop_ = true; if (render_thread_.joinable()) render_thread_.join(); + // wait for render thread to stop + while (render_thread_running_) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + spdlog::info("port_audio render thread stopped"); } void audio_device_manager::render_thread() { + set_thread_name("audio_render"); + render_thread_running_ = true; dummy_track* master = g_mixer.get_master(); - spdlog::info("port_audio render thread started"); render_buffers_.resize(get_output_channel_count()); @@ -137,7 +147,7 @@ void audio_device_manager::render_thread() { for (auto& render_buffer : render_buffers_) render_buffer.SetCapacity(frames * 3); - while (audio_->isStreamRunning()) { + while (!render_thread_should_stop_) { // const float milliseconds = 1.f / (rate / (frames / 4)) * 1e4; g_audio_thread_hub.process_messages(); @@ -157,5 +167,5 @@ void audio_device_manager::render_thread() { render_buffers_[i].Push(element, frames); } } - spdlog::info("port_audio render thread stopped"); + render_thread_running_ = false; } diff --git a/core/audio/device/audio_device_manager.h b/core/audio/device/audio_device_manager.h index 143a046..018f426 100644 --- a/core/audio/device/audio_device_manager.h +++ b/core/audio/device/audio_device_manager.h @@ -31,6 +31,7 @@ protected: std::thread render_thread_; std::vector> render_buffers_; std::atomic_bool render_thread_running_ = false; + std::atomic_bool render_thread_should_stop_ = false; #pragma endregion private: RtAudio::StreamParameters input_params_ = {}; diff --git a/core/audio/misc/audio_buffer.cpp b/core/audio/misc/audio_buffer.cpp index 5bd54cd..90e1aac 100644 --- a/core/audio/misc/audio_buffer.cpp +++ b/core/audio/misc/audio_buffer.cpp @@ -1,10 +1,8 @@ #include "audio_buffer.h" #include -#include void audio_buffer::resize(uint32_t channel_num, uint32_t block_size) { - ZoneScoped; std::scoped_lock lock(lock_); buffer_.resize(channel_num); headers_.resize(channel_num); @@ -15,7 +13,6 @@ void audio_buffer::resize(uint32_t channel_num, uint32_t block_size) { } void audio_buffer::clear() { - ZoneScoped; std::scoped_lock lock(lock_); for (auto& channel : buffer_) { std::memset(channel.data(), 0, channel.size() * sizeof(float)); @@ -23,7 +20,6 @@ void audio_buffer::clear() { } void audio_buffer::mix(audio_buffer& in_buffer, float percent) { - ZoneScoped; std::scoped_lock lock(lock_); // will be optimized by compiler for (uint32_t channel_index = 0; channel_index < buffer_.size(); channel_index++) { @@ -36,7 +32,6 @@ void audio_buffer::mix(audio_buffer& in_buffer, float percent) { } void audio_buffer::multiple(float percent) { - ZoneScoped; std::scoped_lock lock(lock_); // will be optimized by compiler for (auto& channel : buffer_) { @@ -47,7 +42,6 @@ void audio_buffer::multiple(float percent) { } std::vector audio_buffer::get_interleaved_buffer() { - ZoneScoped; std::scoped_lock lock(lock_); std::vector result; result.reserve(buffer_[0].size() * buffer_.size()); diff --git a/core/audio/mixer/channel_node.h b/core/audio/mixer/channel_node.h index d899f54..cbbaa49 100644 --- a/core/audio/mixer/channel_node.h +++ b/core/audio/mixer/channel_node.h @@ -44,7 +44,7 @@ class null_channel_node : public channel_node { public: null_channel_node() : channel_node(nullptr, channel_node_type::null) {} static void init() { instance = new null_channel_node(); } - static void destroy() { delete instance; } + static void destroy() { delete instance; instance = nullptr; } static null_channel_node* get() { return instance; } std::string get_name() override { return "NullChannelNode"; } diff --git a/core/audio/mixer/mixer.cpp b/core/audio/mixer/mixer.cpp index dcc51b2..469f6f2 100644 --- a/core/audio/mixer/mixer.cpp +++ b/core/audio/mixer/mixer.cpp @@ -1,7 +1,5 @@ #include "mixer.h" -#include - #include "channel_interface.h" #include "channel_node.h" #include "mixer_track.h" @@ -14,7 +12,6 @@ IMPL_SINGLETON_INSTANCE(mixer) void build_effect_channel_interface(mixer_track* track, const channel_interface* in_interface, std::map& processed_tracks) { - ZoneScoped; int32_t& track_current_layer = processed_tracks[track]; auto& input_channel_nodes = in_interface->input_channel_nodes; auto& output_channel_nodes = in_interface->output_channel_nodes; @@ -42,7 +39,6 @@ void build_effect_channel_interface(mixer_track* track, const channel_interface* } int32_t build_process_node_internal(mixer_track* track, std::map& processed_tracks, int32_t layer) { - ZoneScoped; int32_t& track_current_layer = processed_tracks[track]; track_current_layer = std::max(track_current_layer, layer); for (const mixer_track_link& child_link: track->children) { @@ -62,10 +58,9 @@ void build_instrument_process_node(const plugin_host* host, std::map::init(initliazer); null_channel_node::init(); - executor_ = new tf::Executor(std::thread::hardware_concurrency()); + executor_.exchange(new tf::Executor(std::thread::hardware_concurrency())); auto device_manager = initliazer.require(); zero_track = new dummy_track(); @@ -79,19 +74,18 @@ void mixer::init(singleton_initliazer& initliazer) { } void mixer::release(singleton_release_guard& release_guard) { - ZoneScoped; singleton_t::release(release_guard); + release_guard.require_release(); null_channel_node::destroy(); - for (const mixer_track* track : tracks_) { delete track; } + tracks_.clear(); delete zero_track; - delete executor_; + zero_track = nullptr; } dummy_track* mixer::create_dummy_track(const std::string& in_name) { - ZoneScoped; auto* track = new dummy_track(); track->rename(in_name); track->init(); @@ -108,7 +102,6 @@ dummy_track* mixer::create_dummy_track(const std::string& in_name) { } instrument_track* mixer::create_instrument_track(plugin_host* in_instrument) { - ZoneScoped; auto* track = new instrument_track(in_instrument); track->rename(in_instrument->name); track->init(); @@ -131,21 +124,10 @@ void mixer::remove_track(mixer_track* track) { } void mixer::process(uint32_t in_frames) { - ZoneScoped; - tf::Taskflow taskflow; - tf::Task previous_task = taskflow.emplace([] {}); - for (const auto& order: layer_order_) { - for (const auto& layer = layer_tracks_[order]; const auto& track: layer) { - taskflow.emplace([track, in_frames] { - track->process(in_frames); - }).succeed(previous_task); - } - tf::Task new_layer = taskflow.emplace([] {}); - new_layer.succeed(previous_task); - previous_task = new_layer; - } - - executor_->run(taskflow).wait(); + auto e = executor_.load(); + if (!e) + return; + e->run(taskflow_).wait(); post_process(in_frames); dummy_track* master = get_master(); master->buffer.multiple(master->volume); @@ -158,7 +140,6 @@ void mixer::reset() { } void mixer::build_process_node() { - ZoneScoped; if (tracks_.empty()) return; std::map processed_tracks; @@ -183,6 +164,7 @@ void mixer::build_process_node() { void mixer::request_build_process_node() { g_audio_thread_hub.push_message([this] { build_process_node(); + update_taskflow(g_audio_device_manager.get_buffer_size()); }); } @@ -199,7 +181,6 @@ void post_process_internal(mixer_track* track, uint32_t in_frames, std::vector processed_tracks; post_process_internal(master, in_frames, processed_tracks); @@ -208,6 +189,7 @@ void mixer::post_process(uint32_t in_frames) const { void mixer::thread_register_track(mixer_track* track) { tracks_.push_back(track); build_process_node(); + update_taskflow(g_audio_device_manager.get_buffer_size()); on_add_track.broadcast(track); g_main_thread_hub.push_message([track, this] { on_add_track_main_thread.broadcast(track); @@ -220,6 +202,7 @@ void mixer::thread_remove_track(mixer_track* track) { } get_master()->remove_child(track); build_process_node(); + update_taskflow(g_audio_device_manager.get_buffer_size()); on_remove_track.broadcast(track); g_main_thread_hub.push_message([track, this]() { @@ -227,3 +210,26 @@ void mixer::thread_remove_track(mixer_track* track) { delete track; }); } + +void mixer::update_taskflow(uint32_t in_frames) { + tf::Taskflow taskflow; + tf::Task previous_task = taskflow.emplace([] {}); + for (const auto& order: layer_order_) { + for (const auto& layer = layer_tracks_[order]; const auto& track: layer) { + taskflow.emplace([track, in_frames] { + track->process(in_frames); + }).succeed(previous_task); + } + tf::Task new_layer = taskflow.emplace([] {}); + new_layer.succeed(previous_task); + previous_task = new_layer; + } + taskflow_ = std::move(taskflow); +} + +void mixer::begin_release(singleton_release_guard &release_guard) { + singleton::begin_release(release_guard); + auto e = executor_.exchange(nullptr); + e->wait_for_all(); + delete e; +} diff --git a/core/audio/mixer/mixer.h b/core/audio/mixer/mixer.h index dc2b84a..d4c12ad 100644 --- a/core/audio/mixer/mixer.h +++ b/core/audio/mixer/mixer.h @@ -17,6 +17,7 @@ class mixer : public singleton_t { public: void init(singleton_initliazer& initliazer) override; + void begin_release(singleton_release_guard &release_guard) override; void release(singleton_release_guard& release_guard) override; const char* get_name() override { return "mixer"; } @@ -43,10 +44,13 @@ private: void thread_register_track(mixer_track* track); void thread_remove_track(mixer_track* track); + void update_taskflow(uint32_t in_frames); + std::vector tracks_; std::map> layer_tracks_; std::vector layer_order_; - tf::Executor* executor_; + std::atomic executor_; + tf::Taskflow taskflow_; }; DEFINE_SINGLETON_INSTANCE(mixer) diff --git a/core/audio/mixer/mixer_track.cpp b/core/audio/mixer/mixer_track.cpp index fd6e61e..73c2d2c 100644 --- a/core/audio/mixer/mixer_track.cpp +++ b/core/audio/mixer/mixer_track.cpp @@ -1,7 +1,5 @@ #include "mixer_track.h" -#include - #include "channel_interface.h" #include "audio/device/audio_device_manager.h" #include "audio/plugin_host/plugin_host.h" @@ -12,7 +10,6 @@ mixer_track::~mixer_track() { } void mixer_track::init() { - ZoneScoped; const uint32_t channel_count = g_audio_device_manager.get_output_channel_count(); buffer.resize(channel_count, g_audio_device_manager.get_buffer_size()); for (int i = 0; i < buffer.get_num_channels(); ++i) { @@ -50,7 +47,6 @@ void mixer_track::remove_child(mixer_track* in_child) { } void mixer_track::process(uint32_t in_frames) { - ZoneScoped; for (auto effect : effects) effect->process(in_frames); buffer.multiple(volume); diff --git a/core/audio/plugin_host/midi_sequencer.cpp b/core/audio/plugin_host/midi_sequencer.cpp index 7a99ab2..5d2cae5 100644 --- a/core/audio/plugin_host/midi_sequencer.cpp +++ b/core/audio/plugin_host/midi_sequencer.cpp @@ -1,7 +1,5 @@ #include "midi_sequencer.h" -#include - #include "vst2/vst2_plugin_host.h" IMPL_SINGLETON_INSTANCE(midi_sequencer) @@ -14,5 +12,4 @@ void midi_sequencer::init(singleton_initliazer& initliazer) { } void midi_sequencer::process(double sample_rate, uint32_t in_frames) { - ZoneScoped; } diff --git a/core/audio/plugin_host/plugin_host_manager.cpp b/core/audio/plugin_host/plugin_host_manager.cpp index 87aed1d..e59a079 100644 --- a/core/audio/plugin_host/plugin_host_manager.cpp +++ b/core/audio/plugin_host/plugin_host_manager.cpp @@ -1,6 +1,5 @@ #include "plugin_host_manager.h" - #include "plugin_host.h" #include "audio/device/audio_device_manager.h" #include "audio/mixer/channel_interface.h" @@ -11,31 +10,27 @@ #include "thread_message/thread_message_hubs.h" #include "vst2/vst2_plugin_host.h" #include "window/window_manager.h" -#include "tracy/tracy.hpp" IMPL_SINGLETON_INSTANCE(plugin_host_manager) void plugin_host_manager::init(singleton_initliazer& initliazer) { - ZoneScoped; singleton_t::init(initliazer); auto* mixer_ptr = initliazer.require(); mixer_ptr->on_remove_track.add_raw(this, &plugin_host_manager::on_mixer_track_removed); - executor_ = new tf::Executor(std::thread::hardware_concurrency()); + executor_.store(new tf::Executor(std::thread::hardware_concurrency())); } void plugin_host_manager::release(singleton_release_guard& release_guard) { - ZoneScoped; singleton_t::release(release_guard); release_guard.require_release(); for (const plugin_host* host: plugin_hosts_) { delete host; } - delete executor_; + plugin_hosts_.clear(); } plugin_host* plugin_host_manager::create_instrument_plugin_host(const char* path) { - ZoneScoped; auto host = load_plugin(path); if (host) { register_instrument_plugin(host); @@ -54,12 +49,12 @@ void plugin_host_manager::remove_instrument_plugin_host(plugin_host* host) { on_instrument_removed.broadcast(host); delete host; }); + update_taskflow(g_audio_device_manager.get_buffer_size()); } }); } plugin_host* plugin_host_manager::load_plugin(const char* path) { - ZoneScoped; auto host = new vst2_plugin_host(); try { host->load_plugin(path); @@ -83,27 +78,39 @@ void plugin_host_manager::register_instrument_plugin(plugin_host* host) { host->channel->set_output_channel(instrument_track->get_channel_interface()->output_channel_nodes); host->owner_tracks.push_back(instrument_track); host->update_channel_node_name(); + update_taskflow(g_audio_device_manager.get_buffer_size()); g_main_thread_hub.push_message([host, this]() { on_instrument_added.broadcast(host); }); }); } -void plugin_host_manager::process(uint32_t in_frames) const { - ZoneScoped; +void plugin_host_manager::process(uint32_t in_frames) { + tf::Executor* executor = executor_.load(); + if (!executor) { + return; + } + executor->run(taskflow_).wait(); +} + +void plugin_host_manager::on_mixer_track_removed(mixer_track* track) { + for (auto host : plugin_hosts_) { + host->channel->remove_track(track); + } +} + +void plugin_host_manager::update_taskflow(uint32_t in_frames) { tf::Taskflow taskflow; for (auto host : plugin_hosts_) { taskflow.emplace([host, in_frames] { host->process(in_frames); }); } - - executor_->run(taskflow).wait(); + taskflow_ = std::move(taskflow); } -void plugin_host_manager::on_mixer_track_removed(mixer_track* track) { - ZoneScoped; - for (auto host : plugin_hosts_) { - host->channel->remove_track(track); - } +void plugin_host_manager::begin_release(singleton_release_guard& release_guard) { + auto executor = executor_.exchange(nullptr); + executor->wait_for_all(); + delete executor; } diff --git a/core/audio/plugin_host/plugin_host_manager.h b/core/audio/plugin_host/plugin_host_manager.h index 31a50cb..39a5a24 100644 --- a/core/audio/plugin_host/plugin_host_manager.h +++ b/core/audio/plugin_host/plugin_host_manager.h @@ -9,6 +9,7 @@ class mixer_track; class CORE_API plugin_host_manager : public singleton_t { public: void init(singleton_initliazer& initliazer) override; + void begin_release(singleton_release_guard& release_guard) override; void release(singleton_release_guard& release_guard) override; @@ -16,7 +17,7 @@ public: void remove_instrument_plugin_host(plugin_host* host); const std::vector& get_plugin_hosts() { return plugin_hosts_; } - void process(uint32_t in_frames) const; + void process(uint32_t in_frames); const char* get_name() override { return "plugin_host_manager"; } @@ -30,7 +31,9 @@ private: std::vector instrument_plugins_{}; std::vector plugin_hosts_{}; - tf::Executor* executor_; + void update_taskflow(uint32_t in_frames); + std::atomic executor_; + tf::Taskflow taskflow_; }; DEFINE_SINGLETON_INSTANCE(plugin_host_manager) diff --git a/core/audio/plugin_host/vst2/vst2_plugin_host.cpp b/core/audio/plugin_host/vst2/vst2_plugin_host.cpp index 27cca2e..3ac94ca 100644 --- a/core/audio/plugin_host/vst2/vst2_plugin_host.cpp +++ b/core/audio/plugin_host/vst2/vst2_plugin_host.cpp @@ -1,7 +1,5 @@ #include "vst2_plugin_host.h" -#include - #include "audio/device/audio_device_manager.h" #include "audio/mixer/channel_interface.h" @@ -212,7 +210,6 @@ void vst2_plugin_host::update_channel_node_name() { void vst2_plugin_host::process(uint32_t frame_num) { // TODO send midi - ZoneScoped; effect_->processReplacing(effect_, channel->get_input_headers(), channel->get_output_headers(), frame_num); } diff --git a/core/misc/platform_thread.h b/core/misc/platform_thread.h new file mode 100644 index 0000000..47eecb3 --- /dev/null +++ b/core/misc/platform_thread.h @@ -0,0 +1,20 @@ +#include +#include +#if PLATFORM_WINDOWS +#include +#include +#elif PLATFORM_LINUX +#include +#elif PLATFORM_MACOS +#include +#endif + +void set_thread_name(const char* name) { +#if PLATFORM_WINDOWS + SetThreadDescription(GetCurrentThread(), std::wstring(name, name + strlen(name)).c_str()); +#elif PLATFORM_LINUX + pthread_setname_np(pthread_self(), name); +#elif PLATFORM_MACOS + pthread_setname_np(name); +#endif +}