修复mixer渲染问题, 绑定渲染线程核心
This commit is contained in:
parent
1f05cae067
commit
0bcc52da17
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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_;
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user