修复mixer渲染问题, 绑定渲染线程核心

This commit is contained in:
Nanako 2024-06-17 20:26:30 +08:00
parent 1f05cae067
commit 0bcc52da17
13 changed files with 112 additions and 102 deletions

View File

@ -129,6 +129,7 @@ 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);
set_thread_affinity(render_thread_, 0);
render_thread_.detach();
spdlog::info("port_audio render thread started");
}

View File

@ -10,6 +10,9 @@ void audio_buffer::resize(uint32_t channel_num, uint32_t block_size) {
buffer_[i].resize(block_size);
headers_[i] = buffer_[i].data();
}
for (auto& channel : buffer_) {
std::memset(channel.data(), 0, channel.size() * sizeof(sample_t));
}
}
void audio_buffer::clear() {
@ -19,12 +22,12 @@ void audio_buffer::clear() {
}
}
void audio_buffer::mix(audio_buffer& in_buffer, float percent) {
void audio_buffer::mix(audio_buffer& from_buffer, float percent) {
std::scoped_lock lock(lock_);
// will be optimized by compiler
for (uint32_t channel_index = 0; channel_index < buffer_.size(); channel_index++) {
auto& channel = buffer_[channel_index];
auto& in_channel = in_buffer.buffer_[channel_index];
auto& in_channel = from_buffer.buffer_[channel_index];
for (uint32_t sample_index = 0; sample_index < channel.size(); sample_index++) {
channel[sample_index] += in_channel[sample_index] * percent;
}

View File

@ -15,7 +15,7 @@ public:
void resize(uint32_t channel_num, uint32_t block_size);
void clear();
void mix(audio_buffer& in_buffer, float percent = 1.f);
void mix(audio_buffer& from_buffer, float percent = 1.f);
void multiple(float percent);
[[nodiscard]] std::vector<sample_t> get_interleaved_buffer();

View File

@ -134,10 +134,11 @@ void channel_interface::remove_track(mixer_track* track) {
}
mixer_channel_interface::mixer_channel_interface(mixer_track* track) : channel_interface(0, 2), track_(track) {
auto* node = new mixer_channel_node(this, track, 0);
set_output_channel(0, node);
node_ = new mixer_channel_node(this, track, 0);
set_output_channel(0, node_);
}
mixer_channel_interface::~mixer_channel_interface() {
delete output_channel_nodes[0];
delete node_;
}

View File

@ -8,6 +8,7 @@
class mixer_track;
class channel_node;
class mixer_channel_node;
class channel_interface {
public:
@ -51,4 +52,5 @@ public:
[[nodiscard]]mixer_track* get_track() const { return track_; }
private:
mixer_track* track_;
mixer_channel_node* node_;
};

View File

@ -73,6 +73,14 @@ void mixer::init(singleton_initliazer& initliazer) {
tracks_.push_back(master);
}
void mixer::begin_release(singleton_release_guard &release_guard) {
singleton::begin_release(release_guard);
auto e = executor_.exchange(nullptr);
if (e)
e->wait_for_all();
delete e;
}
void mixer::release(singleton_release_guard& release_guard) {
singleton_t<mixer>::release(release_guard);
release_guard.require_release<audio_device_manager>();
@ -127,8 +135,10 @@ void mixer::process(uint32_t in_frames) {
auto e = executor_.load();
if (!e)
return;
e->run(taskflow_).wait();
post_process(in_frames);
for (auto& flow: taskflow_) {
e->run(flow).wait();
}
}
void mixer::reset() {
@ -137,13 +147,44 @@ void mixer::reset() {
}
}
void mixer::request_build_process_node() {
g_audio_thread_hub.push_message([this] {
update_tasks();
});
}
void mixer::thread_register_track(mixer_track* track) {
tracks_.push_back(track);
update_tasks();
on_add_track.broadcast(track);
g_main_thread_hub.push_message([track, this] {
on_add_track_main_thread.broadcast(track);
});
}
void mixer::thread_remove_track(mixer_track* track) {
if (const auto it = std::ranges::find(tracks_, track); it != tracks_.end()) {
tracks_.erase(it);
}
get_master()->remove_child(track);
update_tasks();
on_remove_track.broadcast(track);
g_main_thread_hub.push_message([track, this]() {
on_remove_track_main_thread.broadcast(track);
if (track == selected_track)
selected_track = nullptr;
delete track;
});
}
void mixer::build_process_node() {
if (tracks_.empty())
return;
std::unordered_map<mixer_track*, int32_t> processed_tracks;
auto* master = get_master();
build_process_node_internal(master, processed_tracks, 0);
const auto& instruments = g_plugin_host_manager.get_plugin_hosts();
const auto& instruments = g_plugin_host_manager.get_instrument_hosts();
for (const plugin_host* instrument: instruments) {
build_instrument_process_node(instrument, processed_tracks);
}
@ -156,88 +197,25 @@ void mixer::build_process_node() {
for (const auto& pair: layer_tracks_) {
layer_order_.push_back(pair.first);
}
std::ranges::sort(layer_order_);
}
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());
});
}
void post_process_internal(mixer_track* track, uint32_t in_frames, std::set<mixer_track*>& processed_tracks) {
if (std::ranges::find(processed_tracks, track) != processed_tracks.end())
return;
for (const auto& link: track->children) {
post_process_internal(link.track, in_frames, processed_tracks);
track->buffer.mix(link.track->buffer, link.send_level);
}
track->post_process(in_frames);
processed_tracks.emplace(track);
}
void mixer::post_process(uint32_t in_frames) const {
const auto master = get_master();
std::set<mixer_track*> processed_tracks;
post_process_internal(master, in_frames, processed_tracks);
}
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);
});
}
void mixer::thread_remove_track(mixer_track* track) {
if (const auto it = std::ranges::find(tracks_, track); it != tracks_.end()) {
tracks_.erase(it);
}
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]() {
on_remove_track_main_thread.broadcast(track);
if (track == selected_track)
selected_track = nullptr;
delete track;
});
std::ranges::sort(layer_order_, std::greater());
}
void mixer::update_taskflow(uint32_t in_frames) {
tf::Taskflow taskflow;
std::vector<std::vector<tf::Task>> layer_tasks;
for (int32_t i = layer_order_.size() - 1; i >= 0; --i) {
const auto& order = layer_order_[i];
auto new_layer = std::vector<tf::Task>();
for (const auto& layer = layer_tracks_[order]; const auto& track: layer) {
auto t = taskflow.emplace([track, in_frames] {
taskflow_.clear();
for (int32_t order: layer_order_) {
const auto& layer = layer_tracks_[order];
tf::Taskflow taskflow("mixer_update_layer" + std::to_string(order));
// 每层的轨道并行处理
for (mixer_track* track: layer) {
taskflow.emplace([track, in_frames] {
track->process(in_frames);
});
new_layer.push_back(t);
}
layer_tasks.push_back(std::move(new_layer));
taskflow_.push_back(std::move(taskflow));
}
for (int i = layer_tasks.size() - 1; i >= 0; --i) {
tf::Task layer_task = taskflow.emplace([]{});
for (auto& task: layer_tasks[i]) {
task.precede(layer_task);
}
}
taskflow_ = std::move(taskflow);
}
void mixer::begin_release(singleton_release_guard &release_guard) {
singleton::begin_release(release_guard);
auto e = executor_.exchange(nullptr);
if (e)
e->wait_for_all();
delete e;
void mixer::update_tasks() {
build_process_node();
update_taskflow(g_audio_device_manager.get_buffer_size());
}

View File

@ -30,7 +30,6 @@ public:
void reset();
[[nodiscard]] dummy_track* get_master() const { return reinterpret_cast<dummy_track*>(tracks_[0]); }
void build_process_node();
void request_build_process_node();
dummy_track* zero_track; // 用于没有任何音频输出的通道
@ -43,17 +42,18 @@ public:
mixer_track* selected_track = nullptr;
private:
void post_process(uint32_t in_frames) const;
void thread_register_track(mixer_track* track);
void thread_remove_track(mixer_track* track);
void build_process_node();
void update_taskflow(uint32_t in_frames);
void update_tasks();
std::vector<mixer_track*> tracks_;
std::unordered_map<int32_t, std::vector<mixer_track*>> layer_tracks_;
std::vector<int32_t> layer_order_;
std::atomic<tf::Executor*> executor_;
tf::Taskflow taskflow_;
std::vector<tf::Taskflow> taskflow_;
};
DEFINE_SINGLETON_INSTANCE(mixer)

View File

@ -18,7 +18,6 @@ void mixer_track::init() {
buffer.resize(channel_count, g_audio_device_manager.get_buffer_size());
for (int i = 0; i < buffer.get_num_channels(); ++i) {
ui_buffers->emplace_back(g_audio_device_manager.get_buffer_size() * 2);
ui_buffer_peaks.emplace_back();
}
channel_interface_ = new mixer_channel_interface(this);
@ -59,9 +58,13 @@ void mixer_track::remove_child(mixer_track* in_child) {
}
void mixer_track::process(uint32_t in_frames) {
// 从children中获取buffer
for (const auto& link : children) {
buffer.mix(link.track->buffer, link.send_level);
}
for (auto effect : effects)
effect->process(in_frames);
// buffer.multiple(volume);
post_process(in_frames);
on_processed.broadcast(this);
}
@ -84,7 +87,7 @@ std::string instrument_track::get_name() const {
void delete_effect(mixer_track* track, plugin_host* host) {
track->remove_effect(host);
host->try_close_editor();
g_audio_thread_hub.push_message([host](){
g_audio_thread_hub.push_message([host, track](){
delete host;
});
}

View File

@ -18,16 +18,13 @@ struct mixer_track_link {
float send_level;
};
struct mixer_track_peak {
sample_t peak;
float left_time;
};
class CORE_API mixer_track {
public:
explicit mixer_track(mixer_track_type in_type) : type_(in_type) {
explicit mixer_track(mixer_track_type in_type) : type_(in_type), id(gen_uid()) {
ui_buffers = std::make_shared<circular_buffer_vector_type>();
}
uint64_t get_uid() const { return id; }
virtual ~mixer_track();
void init();
@ -51,6 +48,10 @@ public:
void set_volume(float in_volume) { volume = in_volume; }
[[nodiscard]] float get_volume() const { return volume; }
[[nodiscard]] std::string get_imgui_id() const {
return get_name() + "##" + std::to_string(id);
}
audio_buffer buffer;
std::vector<plugin_host*> effects{};
std::vector<mixer_track_link> children{};
@ -60,11 +61,11 @@ public:
// UI
std::shared_ptr<circular_buffer_vector_type> ui_buffers;
std::vector<mixer_track_peak> ui_buffer_peaks;
private:
const mixer_track_type type_;
std::atomic<float> volume = 1.0f;
channel_interface* channel_interface_ = nullptr;
const uint64_t id = 0;
};
class instrument_track : public mixer_track {

View File

@ -20,12 +20,12 @@ struct host_param_info {
class CORE_API plugin_host {
friend class window_manager;
public:
plugin_host() {
static uint32_t id_counter = 0;
id = ++id_counter;
plugin_host() : id(gen_uid()) {
}
virtual ~plugin_host();
uint64_t get_uid() const { return id; }
virtual bool load_plugin(const char* path) = 0;
virtual void set_enabled(bool enabled) = 0;
@ -79,5 +79,5 @@ protected:
virtual void on_close_editor() = 0;
private:
bool editor_opened = false;
uint32_t id = 0;
const uint64_t id = 0;
};

View File

@ -106,7 +106,7 @@ void plugin_host_manager::on_mixer_track_removed(mixer_track* track) {
void plugin_host_manager::update_taskflow(uint32_t in_frames) {
tf::Taskflow taskflow;
for (auto host : plugin_hosts_) {
for (auto host : instrument_plugins_) {
taskflow.emplace([host, in_frames] {
host->process(in_frames);
});

View File

@ -1,4 +1,6 @@
#pragma once
#include <chrono>
#include <cstdint>
#if PLATFORM_WINDOWS
#ifdef core_EXPORTS
@ -21,3 +23,10 @@
#else
typedef float sample_t;
#endif
static uint64_t gen_uid() {
static uint64_t uid = 0;
const uint64_t current_uid = ++uid;
// append the current time to the uid to make it unique
return (current_uid << 32) | std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}

View File

@ -9,7 +9,7 @@
#include <pthread.h>
#endif
void set_thread_name(const char* name) {
inline void set_thread_name(const char* name) {
#if PLATFORM_WINDOWS
SetThreadDescription(GetCurrentThread(), std::wstring(name, name + strlen(name)).c_str());
#elif PLATFORM_LINUX
@ -18,3 +18,15 @@ void set_thread_name(const char* name) {
pthread_setname_np(name);
#endif
}
inline void set_thread_affinity(std::thread& thread, int core) {
#if PLATFORM_WINDOWS
const auto handle = reinterpret_cast<HANDLE>(thread.native_handle());
SetThreadAffinityMask(handle, 1 << core);
#elif PLATFORM_LINUX || PLATFORM_MACOS
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(core, &cpuset);
pthread_setaffinity_np(thread.native_handle(), sizeof(cpu_set_t), &cpuset);
#endif
}