1. 优化内存池
2. 延迟补偿功能完成 3. 优化内存布局
This commit is contained in:
parent
c4abac61db
commit
e65c44899a
@ -36,6 +36,5 @@ set(TF_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(third_party/rtaudio)
|
||||
add_subdirectory(third_party/spdlog)
|
||||
add_subdirectory(third_party/mempool)
|
||||
add_subdirectory(third_party/taskflow)
|
||||
add_subdirectory(third_party/glfw)
|
||||
|
@ -1,15 +1,15 @@
|
||||
|
||||
function(retrieve_files path out_files)
|
||||
function(retrieve_files_custom path extension out_files)
|
||||
message(STATUS "Retrieving files in ${path}")
|
||||
set(EXTENSIONS "")
|
||||
foreach(ext ${extension})
|
||||
list(APPEND EXTENSIONS "${path}/*.${ext}")
|
||||
endforeach ()
|
||||
|
||||
# 递归查找文件夹下的 .h .hpp. ini 文件保存到 HEAD_FILES
|
||||
file(GLOB_RECURSE HEAD_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS ${path}/*.h ${path}/*.hpp ${path}/*.ini)
|
||||
|
||||
# 递归查找文件夹下的 *.cpp *.c 文件保存到 SRC_FILES
|
||||
file(GLOB_RECURSE SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS ${path}/*.cpp ${path}*.c ${path}*.ixx)
|
||||
|
||||
file(GLOB_RECURSE FIND_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS ${EXTENSIONS})
|
||||
# 将 HEDADER_FILES 和 SRC_FILES 保存到 ALL_FILES 变量
|
||||
set(ALL_FILES ${HEAD_FILES} ${SRC_FILES})
|
||||
set(ALL_FILES ${FIND_FILES})
|
||||
|
||||
set(RESULT "")
|
||||
|
||||
@ -53,3 +53,9 @@ function(retrieve_files path out_files)
|
||||
|
||||
set(${out_files} ${RESULT} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(retrieve_files path out_files)
|
||||
set(temp_files "")
|
||||
retrieve_files_custom(${path} "h;hpp;ini;cpp;c;ixx" temp_files)
|
||||
set(${out_files} ${temp_files} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
@ -10,7 +10,7 @@ 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)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC rtaudio spdlog Taskflow glfw)
|
||||
|
||||
option(USE_DOUBLE_SAMPLE "Use double sample" OFF)
|
||||
add_definitions(-Dcore_EXPORTS)
|
||||
@ -27,27 +27,27 @@ endif ()
|
||||
# platform micros
|
||||
if (WIN32)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC PLATFORM_WINDOWS=1 PLATFORM_MACOS=0 PLATFORM_LINUX=0 GLFW_EXPOSE_NATIVE_WIN32)
|
||||
elseif(APPLE)
|
||||
elseif (APPLE)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC PLATFORM_WINDOWS=0 PLATFORM_MACOS=1 PLATFORM_LINUX=0 GLFW_EXPOSE_NATIVE_COCOA)
|
||||
elseif(UNIX AND NOT 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()
|
||||
endif ()
|
||||
|
||||
# cpu amd or arm
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*")
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC CPU_AMD64=1 CPU_ARM=0)
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|ARM.*")
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC CPU_AMD64=0 CPU_ARM=1)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC BUILD_DEBUG=1)
|
||||
else()
|
||||
else ()
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC BUILD_DEBUG=0)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (USE_DOUBLE_SAMPLE)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC USE_DOUBLE_SAMPLE=1)
|
||||
else()
|
||||
else ()
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC USE_DOUBLE_SAMPLE=0)
|
||||
endif ()
|
@ -27,9 +27,7 @@ public:
|
||||
|
||||
bool update_application();
|
||||
|
||||
virtual void tick(float delta_time)
|
||||
{
|
||||
}
|
||||
virtual void tick(float delta_time) {}
|
||||
|
||||
protected:
|
||||
};
|
||||
|
@ -24,8 +24,8 @@ void audio_device_manager::init(singleton_initliazer& initliazer) {
|
||||
singleton_t<audio_device_manager>::init(initliazer);
|
||||
audio_ = new RtAudio();
|
||||
log_all_devices();
|
||||
output_params_.deviceId = audio_->getDefaultOutputDevice();
|
||||
// output_params_.deviceId = 131;
|
||||
// output_params_.deviceId = audio_->getDefaultOutputDevice();
|
||||
output_params_.deviceId = 131;
|
||||
output_params_.nChannels = get_output_channel_count();
|
||||
output_params_.firstChannel = 0;
|
||||
buffer_size_ = 512;
|
||||
|
@ -155,7 +155,7 @@ std::vector<sample_t> audio_buffer::get_interleaved_buffer() const {
|
||||
|
||||
void audio_buffer::free() {
|
||||
for (sample_t* header : headers_)
|
||||
get_audio_buffer_pool()->free(header);
|
||||
get_audio_buffer_pool()->free(header, frame_size_);
|
||||
headers_.clear();
|
||||
}
|
||||
|
||||
|
@ -7,15 +7,18 @@
|
||||
|
||||
class CORE_API audio_buffer {
|
||||
public:
|
||||
static void(*add_func)(audio_buffer& in_buffer, audio_buffer& from_buffer, float percent);
|
||||
static void(*multiple_func)(audio_buffer& in_buffer, float percent);
|
||||
static void (*add_func)(audio_buffer& in_buffer, audio_buffer& from_buffer, float percent);
|
||||
|
||||
static void (*multiple_func)(audio_buffer& in_buffer, float percent);
|
||||
|
||||
audio_buffer();
|
||||
|
||||
audio_buffer(audio_buffer&& in_buffer) noexcept {
|
||||
headers_ = std::move(in_buffer.headers_);
|
||||
frame_size_ = in_buffer.frame_size_;
|
||||
in_buffer.frame_size_ = 0;
|
||||
}
|
||||
|
||||
audio_buffer& operator=(audio_buffer&& in_buffer) noexcept {
|
||||
if (this != &in_buffer) {
|
||||
headers_ = std::move(in_buffer.headers_);
|
||||
@ -24,23 +27,28 @@ public:
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~audio_buffer();
|
||||
|
||||
sample_t** get_headers() { return headers_.data(); }
|
||||
const std::vector<sample_t*>& get_headers_vector() { return headers_; }
|
||||
[[nodiscard]] auto get_headers() -> sample_t** { return headers_.data(); }
|
||||
[[nodiscard]] auto get_headers_vector() const -> const std::vector<sample_t*>& { return headers_; }
|
||||
|
||||
[[nodiscard]] uint32_t get_num_channels() const { return headers_.size(); }
|
||||
[[nodiscard]] uint32_t get_num_samples() const { return frame_size_; }
|
||||
[[nodiscard]] auto get_num_channels() const -> uint32_t { return headers_.size(); }
|
||||
[[nodiscard]] auto get_num_samples() const -> uint32_t { return frame_size_; }
|
||||
|
||||
void resize(uint32_t channel_num, uint32_t frame_size);
|
||||
|
||||
void clear();
|
||||
|
||||
void add(audio_buffer& from_buffer, float percent = 1.f);
|
||||
|
||||
void multiple(float percent);
|
||||
|
||||
[[nodiscard]] std::vector<sample_t> get_interleaved_buffer() const;
|
||||
|
||||
private:
|
||||
void free();
|
||||
|
||||
private:
|
||||
std::vector<sample_t*> headers_{};
|
||||
std::mutex lock_{};
|
||||
|
@ -1,15 +1,26 @@
|
||||
#include "audio_buffer_pool.h"
|
||||
|
||||
#include "audio/mixer/mixer.h"
|
||||
|
||||
IMPL_SINGLETON_INSTANCE(audio_buffer_pool)
|
||||
|
||||
void audio_buffer_pool::init(singleton_initliazer& initliazer) {
|
||||
}
|
||||
|
||||
sample_t * audio_buffer_pool::alloc(uint32_t block_size) {
|
||||
auto* alloc_block = static_cast<sample_t*>(pool_.alloc(block_size * sizeof(sample_t)));
|
||||
return alloc_block;
|
||||
void audio_buffer_pool::release(singleton_release_guard& release_guard) {
|
||||
singleton_t<audio_buffer_pool>::release(release_guard);
|
||||
for (auto& pool : pool_map_) {
|
||||
delete pool.second;
|
||||
}
|
||||
}
|
||||
|
||||
void audio_buffer_pool::free(sample_t* block) {
|
||||
pool_.free(block);
|
||||
sample_t* audio_buffer_pool::alloc(uint32_t block_size) {
|
||||
if (pool_map_.find(block_size) == pool_map_.end()) {
|
||||
pool_map_.emplace(block_size, new memory_pool(sizeof(sample_t) * block_size, 256));
|
||||
}
|
||||
return static_cast<sample_t*>(pool_map_[block_size]->allocate());
|
||||
}
|
||||
|
||||
void audio_buffer_pool::free(sample_t* block, uint32_t block_size) {
|
||||
pool_map_[block_size]->deallocate(block);
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
#pragma once
|
||||
#include "mempool.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../../misc/mempool.h"
|
||||
#include "misc/singleton/singleton.h"
|
||||
|
||||
class CORE_API audio_buffer_pool : public singleton_t<audio_buffer_pool> {
|
||||
public:
|
||||
void init(singleton_initliazer& initliazer) override;
|
||||
void release(singleton_release_guard& release_guard) override;
|
||||
const char* get_name() override { return "audio_buffer_pool"; }
|
||||
|
||||
sample_t* alloc(uint32_t block_size);
|
||||
void free(sample_t* block);
|
||||
void free(sample_t* block, uint32_t block_size);
|
||||
protected:
|
||||
[[nodiscard]] mempool<>& get_pool() { return pool_; }
|
||||
private:
|
||||
mempool<> pool_ = mempool(1024000 * 4); // 一个4MB的内存池, 如果一个缓冲区大小为1024个样本, 那么这个内存池可以分配1024 * 4个缓冲区
|
||||
std::unordered_map<uint32_t, memory_pool*> pool_map_;
|
||||
};
|
||||
|
||||
DEFINE_SINGLETON_INSTANCE(audio_buffer_pool)
|
||||
|
@ -14,5 +14,5 @@ mixer_channel_node::mixer_channel_node(channel_interface* in_owner, mixer_track*
|
||||
}
|
||||
|
||||
const std::vector<sample_t*>& null_channel_node::get_channel_headers() {
|
||||
return g_mixer.zero_track.get_headers_vector();
|
||||
return g_mixer.get_zero_track()->get_headers_vector();
|
||||
}
|
||||
|
@ -17,9 +17,11 @@ class channel_node {
|
||||
public:
|
||||
virtual ~channel_node() = default;
|
||||
|
||||
channel_node(channel_interface* in_owner, channel_node_type in_type) : owner(in_owner), type(in_type) {}
|
||||
channel_node(channel_interface* in_owner, channel_node_type in_type) : owner(in_owner)
|
||||
, type(in_type) {}
|
||||
|
||||
virtual const std::vector<sample_t*>& get_channel_headers() = 0;
|
||||
|
||||
virtual std::string get_name() = 0;
|
||||
|
||||
channel_interface* owner;
|
||||
@ -34,6 +36,7 @@ public:
|
||||
std::string get_name() override { return "MixerChannelNode"; }
|
||||
|
||||
[[nodiscard]] mixer_track* get_track() const { return track_; }
|
||||
|
||||
private:
|
||||
mixer_track* track_;
|
||||
std::vector<sample_t*> channel_headers_;
|
||||
@ -42,10 +45,16 @@ private:
|
||||
|
||||
class null_channel_node : public channel_node {
|
||||
static null_channel_node* instance;
|
||||
|
||||
public:
|
||||
null_channel_node() : channel_node(nullptr, channel_node_type::null) {}
|
||||
static void init() { instance = new null_channel_node(); }
|
||||
static void destroy() { delete instance; instance = nullptr; }
|
||||
|
||||
static void destroy() {
|
||||
delete instance;
|
||||
instance = nullptr;
|
||||
}
|
||||
|
||||
static null_channel_node* get() { return instance; }
|
||||
std::string get_name() override { return "NullChannelNode"; }
|
||||
|
||||
|
@ -27,7 +27,7 @@ void latency_compensator::set_latency(uint32_t in_latency) {
|
||||
latency_ = in_latency;
|
||||
|
||||
const uint32_t block_size = g_audio_device_manager.get_buffer_size();
|
||||
const int32_t block_num = in_latency / block_size + 1;
|
||||
const int32_t block_num = std::ceil((float)in_latency / block_size) + 1;
|
||||
resize(block_num * block_size);
|
||||
push_zeros(in_latency);
|
||||
}
|
||||
@ -51,9 +51,9 @@ void latency_compensator::pop(uint64_t delta_sample, circular_buffer_vector_type
|
||||
}
|
||||
}
|
||||
|
||||
void latency_compensator::pop(uint64_t delta_smaple, audio_buffer& out_buffer) {
|
||||
void latency_compensator::pop(audio_buffer& out_buffer) {
|
||||
for (auto buffer : std::views::zip(internal_buffer, out_buffer.get_headers_vector())) {
|
||||
auto& [internal, target_buffer] = buffer;
|
||||
internal.pop(target_buffer, delta_smaple);
|
||||
internal.pop(target_buffer, out_buffer.get_num_samples());
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ public:
|
||||
|
||||
void push(audio_buffer& in_buffer);
|
||||
void pop(uint64_t delta_sample, circular_buffer_vector_type& out_buffer);
|
||||
void pop(uint64_t delta_smaple, audio_buffer& out_buffer);
|
||||
void pop(audio_buffer& out_buffer);
|
||||
|
||||
void set_latency(uint32_t in_latency);
|
||||
[[nodiscard]] uint32_t get_latency() const { return latency_; }
|
||||
|
@ -4,10 +4,7 @@
|
||||
#include "channel_node.h"
|
||||
#include "audio/device/audio_device_manager.h"
|
||||
#include "audio/misc/audio_buffer_pool.h"
|
||||
#include "audio/plugin_host/plugin_host.h"
|
||||
#include "audio/plugin_host/plugin_host_manager.h"
|
||||
#include "misc/query_timer.h"
|
||||
#include "misc/taskflow_singleton.h"
|
||||
#include "thread_message/thread_message_hubs.h"
|
||||
|
||||
IMPL_SINGLETON_INSTANCE(mixer)
|
||||
@ -20,12 +17,14 @@ void mixer::init(singleton_initliazer& initliazer) {
|
||||
|
||||
null_channel_node::init();
|
||||
|
||||
zero_track.rename("zero");
|
||||
zero_track.init();
|
||||
zero_track_ = new dummy_track();
|
||||
zero_track_->rename("zero");
|
||||
zero_track_->init();
|
||||
|
||||
auto* master = obj_mempool<dummy_track>::alloc();
|
||||
master->rename("master");
|
||||
push_track(master);
|
||||
master_ = new dummy_track();
|
||||
master_->rename("master");
|
||||
master_->init();
|
||||
push_track(master_);
|
||||
}
|
||||
|
||||
void mixer::begin_release(singleton_release_guard &release_guard) {
|
||||
@ -37,13 +36,15 @@ void mixer::release(singleton_release_guard& release_guard) {
|
||||
singleton_t<mixer>::release(release_guard);
|
||||
release_guard.require_release<audio_device_manager>();
|
||||
null_channel_node::destroy();
|
||||
obj_mempool<dummy_track>::free_all();
|
||||
obj_mempool<instrument_track>::free_all();
|
||||
for (auto track : tracks_) {
|
||||
delete track;
|
||||
}
|
||||
delete zero_track_;
|
||||
tracks_.clear();
|
||||
}
|
||||
|
||||
dummy_track* mixer::create_dummy_track(const std::string& in_name) {
|
||||
auto* track = obj_mempool<dummy_track>::alloc();
|
||||
auto* track = new dummy_track();
|
||||
track->init();
|
||||
track->rename(in_name);
|
||||
|
||||
@ -55,7 +56,7 @@ dummy_track* mixer::create_dummy_track(const std::string& in_name) {
|
||||
}
|
||||
|
||||
instrument_track* mixer::create_instrument_track(plugin_host* in_instrument) {
|
||||
auto* track = obj_mempool<instrument_track>::alloc(in_instrument);
|
||||
auto* track = new instrument_track(in_instrument);
|
||||
track->init();
|
||||
|
||||
// register track
|
||||
@ -78,16 +79,7 @@ void mixer::remove_track(mixer_track* track) {
|
||||
on_remove_track_main_thread.broadcast(track);
|
||||
if (track == selected_track)
|
||||
selected_track = nullptr;
|
||||
switch (track->get_type()) {
|
||||
case mixer_track_type::unknown:
|
||||
break;
|
||||
case mixer_track_type::dummy:
|
||||
obj_mempool<dummy_track>::free(dynamic_cast<dummy_track*>(track));
|
||||
break;
|
||||
case mixer_track_type::instrument:
|
||||
obj_mempool<instrument_track>::free(dynamic_cast<instrument_track*>(track));
|
||||
break;
|
||||
}
|
||||
delete track;
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -128,5 +120,7 @@ void mixer::thread_register_track(mixer_track* track) {
|
||||
}
|
||||
|
||||
void mixer::on_mixer_latency_changed() {
|
||||
processor_.update_latency();
|
||||
g_audio_thread_hub.push_message([this] {
|
||||
processor_.update_latency();
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "mempool.h"
|
||||
#include "../../misc/mempool.h"
|
||||
#include "mixer_processor.h"
|
||||
#include "mixer_track.h"
|
||||
#include "taskflow/taskflow.hpp"
|
||||
@ -30,10 +30,10 @@ public:
|
||||
void process(uint32_t in_frames, circular_buffer_vector_type& out_buffer);
|
||||
void reset();
|
||||
|
||||
[[nodiscard]] dummy_track* get_master() const { return reinterpret_cast<dummy_track*>(tracks_[0]); }
|
||||
[[nodiscard]] auto get_master() const -> dummy_track* { return master_; }
|
||||
[[nodiscard]] auto get_zero_track() const -> dummy_track* { return zero_track_; }
|
||||
void request_build_process_node();
|
||||
|
||||
dummy_track zero_track; // 用于没有任何音频输出的通道
|
||||
|
||||
multicast_delegate<mixer_track*> on_add_track;
|
||||
multicast_delegate<mixer_track*> on_add_track_main_thread;
|
||||
@ -48,6 +48,8 @@ private:
|
||||
|
||||
void on_mixer_latency_changed();
|
||||
|
||||
dummy_track* master_;
|
||||
dummy_track* zero_track_; // 用于没有任何音频输出的通道
|
||||
std::vector<mixer_track*> tracks_;
|
||||
mixer_processor processor_;
|
||||
bool process_node_dirty_ = false;
|
||||
|
@ -1,8 +1,10 @@
|
||||
#include "mixer_processor.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "channel_interface.h"
|
||||
#include "channel_node.h"
|
||||
#include "mempool.h"
|
||||
#include "../../misc/mempool.h"
|
||||
#include "mixer.h"
|
||||
#include "mixer_track.h"
|
||||
#include "audio/plugin_host/plugin_host.h"
|
||||
@ -10,12 +12,11 @@
|
||||
#include "misc/taskflow_singleton.h"
|
||||
|
||||
struct build_process_node {
|
||||
explicit build_process_node(std::unordered_map<mixer_track *, std::vector<mixer_track *>> &track_map)
|
||||
: track_map(track_map) {
|
||||
}
|
||||
explicit build_process_node(
|
||||
std::unordered_map<mixer_track*, std::vector<mixer_track_link*>>& in_track_map) : track_map(in_track_map) {}
|
||||
|
||||
void run() {
|
||||
build(g_mixer.get_master());
|
||||
build();
|
||||
build_instruments(g_plugin_host_manager.get_instrument_hosts());
|
||||
|
||||
for (const auto& pair : processed_tracks_) {
|
||||
@ -27,103 +28,143 @@ struct build_process_node {
|
||||
std::ranges::sort(layer_order, std::greater());
|
||||
}
|
||||
|
||||
auto get_result() -> std::unordered_map<int32_t, std::vector<mixer_track*>> {
|
||||
return layer_tracks;
|
||||
}
|
||||
auto get_order() -> std::vector<int32_t> {
|
||||
return layer_order;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<mixer_track*, std::vector<mixer_track_link*>>& track_map;
|
||||
std::unordered_map<mixer_track*, int32_t> processed_tracks_;
|
||||
std::unordered_map<int32_t, std::vector<mixer_track*>> layer_tracks;
|
||||
std::vector<int32_t> layer_order;
|
||||
|
||||
std::unordered_map<mixer_track*, std::vector<mixer_track*>>& track_map;
|
||||
private:
|
||||
int32_t layer = 0;
|
||||
std::unordered_map<mixer_track*, int32_t> processed_tracks_;
|
||||
void build() {
|
||||
build(g_mixer.get_master(), 0);
|
||||
}
|
||||
|
||||
void build(mixer_track* current_track) {
|
||||
void build(mixer_track* current_track, int32_t layer) {
|
||||
int32_t& track_current_layer = processed_tracks_[current_track];
|
||||
track_current_layer = std::max(track_current_layer, layer);
|
||||
const std::vector<mixer_track*>& children = track_map[current_track];
|
||||
for (mixer_track* child : children) {
|
||||
build(child);
|
||||
for (const plugin_host* effect : child->effects) {
|
||||
build_effect_channel_interface(child, effect->channel, processed_tracks_);
|
||||
const auto& child_tracks = track_map[current_track];
|
||||
for (mixer_track_link* link : child_tracks) {
|
||||
mixer_track* child_track = link->from();
|
||||
build(child_track, layer + 1);
|
||||
const auto& effects = child_track->get_effects();
|
||||
for (const plugin_host* effect : effects) {
|
||||
build_effect_channel_interface(child_track, effect->channel, processed_tracks_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_effect_channel_interface(mixer_track *track, const channel_interface *in_interface,
|
||||
std::unordered_map<mixer_track *, int32_t> &processed_tracks) {
|
||||
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;
|
||||
void build_effect_channel_interface(mixer_track* track, const channel_interface* in_interface,
|
||||
std::unordered_map<mixer_track*, int32_t>& processed_tracks) {
|
||||
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;
|
||||
// 如果这个效果器需要从其他轨道输入,那么目标轨道的深度就是这个轨道的深度+1
|
||||
for (int i = 1; i < input_channel_nodes.size(); ++i) {
|
||||
channel_node *node = input_channel_nodes[i];
|
||||
if (node->type != channel_node_type::mixer)
|
||||
continue;
|
||||
const mixer_channel_node *mixer_node = dynamic_cast<mixer_channel_node *>(node);
|
||||
auto *InputTrack = mixer_node->get_track();
|
||||
int32_t &target_mixer_current_layer = processed_tracks[InputTrack];
|
||||
channel_node* node = input_channel_nodes[i];
|
||||
if (node->type != channel_node_type::mixer) continue;
|
||||
const mixer_channel_node* mixer_node = dynamic_cast<mixer_channel_node*>(node);
|
||||
auto* InputTrack = mixer_node->get_track();
|
||||
int32_t& target_mixer_current_layer = processed_tracks[InputTrack];
|
||||
target_mixer_current_layer = std::max(target_mixer_current_layer + 1, track_current_layer + 1);
|
||||
}
|
||||
|
||||
// 如果这个效果器需要输出到其他轨道,那么这个轨道的深度就是目标轨道的深度+1
|
||||
for (int i = 1; i < output_channel_nodes.size(); ++i) {
|
||||
auto *node = output_channel_nodes[i];
|
||||
if (node->type != channel_node_type::mixer)
|
||||
continue;
|
||||
const auto *MixerChannelNode = dynamic_cast<mixer_channel_node *>(node);
|
||||
auto *MixerTrack = MixerChannelNode->get_track();
|
||||
const int32_t &TargetMixerCurrentLayer = processed_tracks[MixerTrack];
|
||||
auto* node = output_channel_nodes[i];
|
||||
if (node->type != channel_node_type::mixer) continue;
|
||||
const auto* MixerChannelNode = dynamic_cast<mixer_channel_node*>(node);
|
||||
auto* MixerTrack = MixerChannelNode->get_track();
|
||||
const int32_t& TargetMixerCurrentLayer = processed_tracks[MixerTrack];
|
||||
track_current_layer = std::max(track_current_layer, TargetMixerCurrentLayer + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void build_instruments(const std::vector<plugin_host*>& instruments) {
|
||||
for (auto instrument: instruments) {
|
||||
for (auto instrument : instruments) {
|
||||
build_instrument_process_node(instrument, processed_tracks_);
|
||||
}
|
||||
}
|
||||
|
||||
void build_instrument_process_node(const plugin_host *host,
|
||||
std::unordered_map<mixer_track *, int32_t> &processed_tracks) {
|
||||
for (mixer_track *Track: host->owner_tracks) {
|
||||
void build_instrument_process_node(const plugin_host* host,
|
||||
std::unordered_map<mixer_track*, int32_t>& processed_tracks) {
|
||||
for (mixer_track* Track : host->owner_tracks) {
|
||||
build_effect_channel_interface(Track, host->channel, processed_tracks);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mixer_track_link::mixer_track_link() {
|
||||
compensator_.init();
|
||||
}
|
||||
|
||||
mixer_track_link::mixer_track_link(mixer_track* from_track, mixer_track* to, float in_send_level): from_(from_track)
|
||||
, send_to_(to)
|
||||
, send_level_(in_send_level) {
|
||||
compensator_.init();
|
||||
}
|
||||
|
||||
void mixer_track_link::process() {
|
||||
audio_buffer& from_buffer = from_->get_buffer();
|
||||
audio_buffer& to_buffer = send_to_->get_buffer();
|
||||
|
||||
audio_buffer temp_mix_buffer;
|
||||
temp_mix_buffer.resize(2, to_buffer.get_num_samples());
|
||||
compensator_.pop(temp_mix_buffer);
|
||||
|
||||
to_buffer.add(temp_mix_buffer, send_level_);
|
||||
compensator_.push(from_buffer);
|
||||
}
|
||||
|
||||
void mixer_processor::process(uint32_t in_frames) {
|
||||
task_frame_ = in_frames;
|
||||
for (auto& flow: taskflow_) {
|
||||
for (auto& flow : taskflow_) {
|
||||
g_executor->run(flow).wait();
|
||||
}
|
||||
}
|
||||
|
||||
void mixer_processor::pop_master(uint32_t in_frames, circular_buffer_vector_type &out_buffer) {
|
||||
g_mixer.get_master();
|
||||
g_mixer.compensator_.pop(in_frames, out_buffer);
|
||||
void mixer_processor::pop_master(uint32_t in_frames, circular_buffer_vector_type& out_buffer) {
|
||||
const auto master = g_mixer.get_master();
|
||||
const auto& buffer = master->get_buffer();
|
||||
const std::vector<sample_t*>& headers = buffer.get_headers_vector();
|
||||
for (const auto& pair : std::ranges::views::zip(headers, out_buffer)) {
|
||||
const auto& [in, out] = pair;
|
||||
out.push(in, in_frames);
|
||||
}
|
||||
}
|
||||
|
||||
void mixer_processor::update_taskflow() {
|
||||
build_process_node build_node(layer_tracks_);
|
||||
build_process_node build_node(track_map_);
|
||||
build_node.run();
|
||||
auto& layer_order = build_node.layer_order;
|
||||
auto& layer_tracks = build_node.layer_tracks;
|
||||
|
||||
auto layer_order = build_node.get_order();
|
||||
auto layer_map = build_node.get_result();
|
||||
|
||||
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 (const auto& order : layer_order) {
|
||||
const auto& track_group = layer_map[order];
|
||||
tf::Taskflow taskflow("mixer_update_layer");
|
||||
|
||||
// 每层的轨道并行处理
|
||||
for (mixer_track* track : layer) {
|
||||
for (mixer_track* track : track_group) {
|
||||
taskflow.emplace([track, this] {
|
||||
auto& track_buffer_header = track;
|
||||
audio_buffer& track_buffer = track->get_buffer();
|
||||
std::vector<mixer_track_link*>& links = track_map_[track];
|
||||
|
||||
// 从children中获取buffer
|
||||
for (const auto &link: children) {
|
||||
link.track->compensator_.pop(in_frames, temp_mix_buffer_);
|
||||
// audio_buffer& child_buffer = link.track->buffer;
|
||||
buffer_.add(temp_mix_buffer_, link.send_level);
|
||||
for (mixer_track_link* link : links) {
|
||||
link->process();
|
||||
}
|
||||
|
||||
track->process(task_frame_);
|
||||
track->push_ui_buffer(track_buffer);
|
||||
});
|
||||
}
|
||||
|
||||
taskflow_.push_back(std::move(taskflow));
|
||||
}
|
||||
}
|
||||
@ -131,45 +172,63 @@ void mixer_processor::update_taskflow() {
|
||||
void mixer_processor::update_latency() {
|
||||
int32_t max_latency = 0;
|
||||
int32_t min_latency = 0;
|
||||
// for (mixer_track* track : tracks_) {
|
||||
// max_latency = std::max<int32_t>(max_latency, track->get_real_latency());
|
||||
// min_latency = std::min<int32_t>(min_latency, track->get_real_latency());
|
||||
// }
|
||||
//
|
||||
// for (size_t i = 1; i < tracks_.size(); i++) {
|
||||
// mixer_track* track = tracks_[i];
|
||||
// track->compensator_.update_buffer(min_latency, max_latency);
|
||||
// }
|
||||
// 计算所有轨道链的最大延迟和最小延迟
|
||||
const auto& all_tracks = g_mixer.get_tracks();
|
||||
const dummy_track* master = g_mixer.get_master();
|
||||
for (mixer_track* track : all_tracks) {
|
||||
if (track == master) continue;
|
||||
int32_t latency = 0;
|
||||
calc_latency(track, latency);
|
||||
max_latency = std::max(max_latency, latency);
|
||||
min_latency = std::min(min_latency, latency);
|
||||
}
|
||||
const int32_t latency_diff = max_latency - min_latency;
|
||||
for (mixer_track_link* link : links_) {
|
||||
if (link->to() != master) continue;
|
||||
const int32_t track_latency = link->from()->get_latency();
|
||||
const int32_t to_master_latency = latency_diff - track_latency;
|
||||
link->compensator().set_latency(to_master_latency);
|
||||
link->from()->set_ui_buffer_latency(to_master_latency);
|
||||
}
|
||||
}
|
||||
|
||||
void mixer_processor::add_link(mixer_track* from, mixer_track* to, float in_send_level) {
|
||||
mixer_track_link* new_link = obj_mempool<mixer_track_link>::alloc(from, to, in_send_level);
|
||||
auto* new_link = new mixer_track_link(from, to, in_send_level);
|
||||
links_.push_back(new_link);
|
||||
|
||||
// 将连接的轨道加入到层次结构中
|
||||
layer_tracks_[from].push_back(to);
|
||||
track_map_[to].push_back(new_link);
|
||||
|
||||
update_all();
|
||||
}
|
||||
|
||||
void mixer_processor::remove_link(mixer_track* from, mixer_track* to) {
|
||||
const auto it = std::ranges::find_if(links_, [from, to](const auto& link) {
|
||||
return link->from == from && link->send_to == to;
|
||||
return link->from() == from && link->to() == to;
|
||||
});
|
||||
if (it != links_.end()) {
|
||||
obj_mempool<mixer_track_link>::free(*it);
|
||||
delete *it;
|
||||
links_.erase(it);
|
||||
|
||||
// 更新层次结构
|
||||
std::ranges::remove(layer_tracks_[from], to);
|
||||
if (layer_tracks_[from].empty()) {
|
||||
layer_tracks_.erase(from);
|
||||
}
|
||||
|
||||
update_all();
|
||||
}
|
||||
}
|
||||
|
||||
void mixer_processor::remove_track(mixer_track *track) {
|
||||
|
||||
void mixer_processor::remove_track(mixer_track* track) {
|
||||
const auto all_links = obj_mempool<mixer_track_link>::objs();
|
||||
for (auto link : all_links) {
|
||||
if (link->from() == track || link->to() == track) {
|
||||
delete link;
|
||||
}
|
||||
}
|
||||
track_map_.erase(track);
|
||||
}
|
||||
|
||||
void mixer_processor::calc_latency(mixer_track* track, int32_t& latency) {
|
||||
latency += track->get_latency();
|
||||
const auto& track_child_links = track_map_[track];
|
||||
for (const mixer_track_link* link : track_child_links) {
|
||||
const mixer_track* target_track = link->to();
|
||||
if (target_track == g_mixer.get_master())
|
||||
continue;
|
||||
mixer_track* child_track = link->from();
|
||||
calc_latency(child_track, latency);
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,31 @@
|
||||
#pragma once
|
||||
#include "latency_compensator.h"
|
||||
#include "misc/mempool.h"
|
||||
#include "taskflow/taskflow.hpp"
|
||||
|
||||
class mixer_track;
|
||||
|
||||
struct mixer_track_link {
|
||||
mixer_track_link() = default;
|
||||
mixer_track_link(mixer_track* from_track, mixer_track* to, float in_send_level) : from(from_track), send_to(to), send_level(in_send_level) {
|
||||
}
|
||||
mixer_track* from = nullptr;
|
||||
mixer_track* send_to = nullptr;
|
||||
float send_level = 1.0f;
|
||||
latency_compensator compensator;
|
||||
struct mixer_track_link : public pool_obj<mixer_track_link> {
|
||||
mixer_track_link();
|
||||
|
||||
mixer_track_link(mixer_track* from_track, mixer_track* to, float in_send_level);
|
||||
|
||||
[[nodiscard]] auto from() const -> mixer_track* { return from_; }
|
||||
[[nodiscard]] auto to() const -> mixer_track* { return send_to_; }
|
||||
[[nodiscard]] auto send_level() const -> float { return send_level_; }
|
||||
[[nodiscard]] auto compensator() -> latency_compensator& { return compensator_; }
|
||||
void process();
|
||||
private:
|
||||
mixer_track* from_ = nullptr;
|
||||
mixer_track* send_to_ = nullptr;
|
||||
float send_level_ = 1.0f;
|
||||
latency_compensator compensator_;
|
||||
};
|
||||
|
||||
class mixer_processor {
|
||||
public:
|
||||
void process(uint32_t in_frames);
|
||||
|
||||
void pop_master(uint32_t in_frames, circular_buffer_vector_type& out_buffer);
|
||||
|
||||
void update_all() {
|
||||
@ -26,14 +35,16 @@ public:
|
||||
|
||||
void update_taskflow();
|
||||
void update_latency();
|
||||
|
||||
void add_link(mixer_track* from, mixer_track* to, float in_send_level);
|
||||
void remove_link(mixer_track* from, mixer_track* to);
|
||||
void remove_track(mixer_track* track);
|
||||
private:
|
||||
void calc_latency(mixer_track* track, int32_t& latency);
|
||||
|
||||
std::vector<tf::Taskflow> taskflow_;
|
||||
std::vector<mixer_track_link*> links_;
|
||||
std::unordered_map<mixer_track*, std::vector<mixer_track*>> layer_tracks_;
|
||||
|
||||
std::atomic<uint32_t> task_frame_ = 0;
|
||||
std::unordered_map<mixer_track*, std::vector<mixer_track_link*>> track_map_; // [to, links] 表示某个轨道会发送到哪些轨道
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
mixer_track::~mixer_track() {
|
||||
delete channel_interface_;
|
||||
channel_interface_ = nullptr;
|
||||
for (auto e : effects) {
|
||||
for (auto e : effects_) {
|
||||
// e->on_latency_changed.remove_all(this);
|
||||
delete_effect(this, e);
|
||||
}
|
||||
@ -19,10 +19,10 @@ void mixer_track::init() {
|
||||
uint32_t block_size = g_audio_device_manager.get_buffer_size();
|
||||
buffer_.resize(channel_count, block_size);
|
||||
|
||||
ui_buffers.resize(channel_count);
|
||||
for (auto& buffer : ui_buffers) {
|
||||
buffer.set_capacity(block_size * 2);
|
||||
}
|
||||
ui_buffers_.resize(channel_count);
|
||||
for (auto& buffer : ui_buffers_) {
|
||||
buffer.set_capacity(block_size * 2);
|
||||
}
|
||||
|
||||
channel_interface_ = new mixer_channel_interface(this);
|
||||
}
|
||||
@ -32,7 +32,7 @@ void mixer_track::add_effect(plugin_host* in_effect) {
|
||||
in_effect->owner_tracks.push_back(this);
|
||||
in_effect->channel->set_input_channel(channel_interface_->output_channel_nodes);
|
||||
in_effect->channel->set_output_channel(channel_interface_->output_channel_nodes);
|
||||
effects.push_back(in_effect);
|
||||
effects_.push_back(in_effect);
|
||||
});
|
||||
}
|
||||
|
||||
@ -41,44 +41,53 @@ void mixer_track::remove_effect(plugin_host* in_effect) {
|
||||
auto remove_effect_track = std::remove(in_effect->owner_tracks.begin(), in_effect->owner_tracks.end(), this);
|
||||
in_effect->owner_tracks.erase(remove_effect_track, in_effect->owner_tracks.end());
|
||||
|
||||
auto remove_effect = std::remove(effects.begin(), effects.end(), in_effect);
|
||||
effects.erase(remove_effect, effects.end());
|
||||
auto remove_effect = std::remove(effects_.begin(), effects_.end(), in_effect);
|
||||
effects_.erase(remove_effect, effects_.end());
|
||||
});
|
||||
}
|
||||
|
||||
void mixer_track::process(uint32_t in_frames) {
|
||||
for (auto effect : effects)
|
||||
effect->process(in_frames);
|
||||
for (auto effect : effects_) effect->process(in_frames);
|
||||
buffer_.multiple(get_volume());
|
||||
}
|
||||
|
||||
int32_t mixer_track::get_latency() const {
|
||||
uint32_t latency = 0;
|
||||
for (const auto effect : effects) {
|
||||
auto mixer_track::get_latency() const -> int32_t {
|
||||
int32_t latency = 0;
|
||||
for (const auto effect : effects_) {
|
||||
latency += effect->get_latency();
|
||||
}
|
||||
return latency;
|
||||
}
|
||||
|
||||
instrument_track::instrument_track(plugin_host* in_instrument): mixer_track(mixer_track_type::instrument), instrument_(in_instrument) {
|
||||
void mixer_track::set_ui_buffer_latency(int32_t in_latency) {
|
||||
const uint32_t block_size = g_audio_device_manager.get_buffer_size();
|
||||
const int32_t block_num = std::ceil((float)in_latency / block_size) + 1;
|
||||
|
||||
for (auto& buffer : ui_buffers_) {
|
||||
buffer.set_capacity(block_num * block_size);
|
||||
buffer.push_zeros(in_latency);
|
||||
}
|
||||
}
|
||||
|
||||
instrument_track::instrument_track(plugin_host* in_instrument): mixer_track(mixer_track_type::instrument)
|
||||
, instrument_(in_instrument) {}
|
||||
|
||||
void instrument_track::rename(const std::string& in_name) {
|
||||
instrument_->name = in_name;
|
||||
}
|
||||
|
||||
std::string instrument_track::get_name() const {
|
||||
auto instrument_track::get_name() const -> std::string {
|
||||
return instrument_->name;
|
||||
}
|
||||
|
||||
int32_t instrument_track::get_latency() const {
|
||||
return mixer_track::get_latency() + instrument_->get_latency();
|
||||
auto instrument_track::get_latency() const -> int32_t {
|
||||
return mixer_track::get_latency() + instrument_->get_latency() + instrument_->get_user_latency();
|
||||
}
|
||||
|
||||
void delete_effect(mixer_track* track, plugin_host* host) {
|
||||
track->remove_effect(host);
|
||||
host->try_close_editor();
|
||||
g_audio_thread_hub.push_message([host, track](){
|
||||
g_audio_thread_hub.push_message([host, track]() {
|
||||
delete host;
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
#include "latency_compensator.h"
|
||||
#include "../../misc/mempool.h"
|
||||
#include "audio/misc/audio_buffer.h"
|
||||
#include "audio/misc/circular_audio_buffer.h"
|
||||
#include "misc/delegates.h"
|
||||
@ -18,70 +20,94 @@ enum class mixer_track_type {
|
||||
|
||||
class CORE_API mixer_track {
|
||||
friend class mixer;
|
||||
|
||||
public:
|
||||
explicit mixer_track(mixer_track_type in_type) : type_(in_type), id_(gen_uid()) {}
|
||||
[[nodiscard]] uint64_t get_uid() const { return id_; }
|
||||
explicit mixer_track(mixer_track_type in_type) : type_(in_type)
|
||||
, id_(gen_uid()) {}
|
||||
|
||||
[[nodiscard]] auto get_uid() const -> uint64_t { return id_; }
|
||||
|
||||
virtual ~mixer_track();
|
||||
|
||||
void init();
|
||||
|
||||
void add_effect(plugin_host* in_effect);
|
||||
|
||||
void remove_effect(plugin_host* in_effect);
|
||||
|
||||
void process(uint32_t in_frames);
|
||||
|
||||
virtual void rename(const std::string& in_name) = 0;
|
||||
[[nodiscard]] virtual std::string get_name() const = 0;
|
||||
|
||||
[[nodiscard]] sample_t** get_headers() { return buffer_.get_headers(); }
|
||||
[[nodiscard]] const std::vector<sample_t*>& get_headers_vector() { return buffer_.get_headers_vector(); }
|
||||
[[nodiscard]] virtual auto get_name() const -> std::string = 0;
|
||||
|
||||
[[nodiscard]] auto get_headers() -> sample_t** { return buffer_.get_headers(); }
|
||||
[[nodiscard]] auto get_headers_vector() -> const std::vector<sample_t*>& { return buffer_.get_headers_vector(); }
|
||||
[[nodiscard]] auto get_buffer() -> audio_buffer& { return buffer_; }
|
||||
void clear() { buffer_.clear(); }
|
||||
|
||||
[[nodiscard]] mixer_track_type get_type() const { return type_; }
|
||||
[[nodiscard]] auto get_type() const -> mixer_track_type { return type_; }
|
||||
|
||||
[[nodiscard]] channel_interface* get_channel_interface() const { return channel_interface_; }
|
||||
[[nodiscard]] auto get_channel_interface() const -> channel_interface* { return channel_interface_; }
|
||||
|
||||
void set_volume(float in_volume) { volume_ = in_volume; }
|
||||
[[nodiscard]] float get_volume() const { return volume_; }
|
||||
[[nodiscard]] auto get_volume() const -> float { return volume_; }
|
||||
|
||||
[[nodiscard]] virtual int32_t get_latency() const;
|
||||
[[nodiscard]] virtual auto get_latency() const -> int32_t;
|
||||
|
||||
[[nodiscard]] std::string get_imgui_id() const {
|
||||
[[nodiscard]] auto get_imgui_id() const -> std::string {
|
||||
return get_name() + "##" + std::to_string(id_);
|
||||
}
|
||||
|
||||
std::vector<plugin_host*> effects{};
|
||||
void set_ui_buffer_latency(int32_t in_latency);
|
||||
|
||||
circular_buffer_vector_type ui_buffers;
|
||||
void push_ui_buffer(const audio_buffer& in_buffer) {
|
||||
auto headers = in_buffer.get_headers_vector();
|
||||
for (auto header : std::ranges::views::zip(headers, ui_buffers_)) {
|
||||
const auto& [in, out] = header;
|
||||
out.push(in, in_buffer.get_num_samples());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_ui_buffer() -> circular_buffer_vector_type& { return ui_buffers_;}
|
||||
[[nodiscard]] auto get_effects() const -> const std::vector<plugin_host*>& { return effects_; }
|
||||
private:
|
||||
audio_buffer buffer_;
|
||||
const mixer_track_type type_;
|
||||
float volume_ = 1.0f;
|
||||
|
||||
circular_buffer_vector_type ui_buffers_;
|
||||
std::vector<plugin_host*> effects_{};
|
||||
audio_buffer buffer_;
|
||||
channel_interface* channel_interface_ = nullptr;
|
||||
const uint64_t id_ = 0;
|
||||
float volume_ = 1.0f;
|
||||
};
|
||||
|
||||
class instrument_track : public mixer_track {
|
||||
class instrument_track : public mixer_track, public pool_obj<instrument_track> {
|
||||
public:
|
||||
static constexpr auto type = mixer_track_type::instrument;
|
||||
|
||||
explicit instrument_track(plugin_host* in_instrument);
|
||||
|
||||
void rename(const std::string& in_name) override;
|
||||
[[nodiscard]] std::string get_name() const override;
|
||||
|
||||
[[nodiscard]] plugin_host* get_instrument() const { return instrument_; }
|
||||
[[nodiscard]] auto get_name() const -> std::string override;
|
||||
|
||||
[[nodiscard]] auto get_instrument() const -> plugin_host* { return instrument_; }
|
||||
|
||||
[[nodiscard]] auto get_latency() const -> int32_t override;
|
||||
|
||||
[[nodiscard]] int32_t get_latency() const override;
|
||||
private:
|
||||
plugin_host* instrument_;
|
||||
};
|
||||
|
||||
class dummy_track : public mixer_track {
|
||||
class dummy_track : public mixer_track, public pool_obj<dummy_track> {
|
||||
public:
|
||||
static constexpr auto type = mixer_track_type::dummy;
|
||||
dummy_track() : mixer_track(mixer_track_type::dummy) {}
|
||||
|
||||
void rename(const std::string& in_name) override { name_ = in_name; }
|
||||
[[nodiscard]] std::string get_name() const override { return name_; }
|
||||
[[nodiscard]] auto get_name() const -> std::string override { return name_; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
};
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include "extern.h"
|
||||
#include "host_param.h"
|
||||
#include "../../misc/mempool.h"
|
||||
#include "audio/misc/circular_audio_buffer.h"
|
||||
#include "misc/delegates.h"
|
||||
|
||||
@ -18,56 +19,79 @@ struct host_param_info {
|
||||
};
|
||||
|
||||
enum class plugin_host_type {
|
||||
vst2,
|
||||
vst3,
|
||||
au,
|
||||
aax,
|
||||
lv2,
|
||||
internal,
|
||||
vst2,
|
||||
vst3,
|
||||
au,
|
||||
aax,
|
||||
lv2,
|
||||
internal,
|
||||
};
|
||||
|
||||
class CORE_API plugin_host {
|
||||
friend class window_manager;
|
||||
|
||||
public:
|
||||
plugin_host(plugin_host_type in_type) : id(gen_uid()), type_(in_type) {
|
||||
}
|
||||
plugin_host(plugin_host_type in_type) : id(gen_uid())
|
||||
, type_(in_type) {}
|
||||
|
||||
virtual ~plugin_host();
|
||||
|
||||
[[nodiscard]] uint64_t get_uid() const { return id; }
|
||||
[[nodiscard]] plugin_host_type get_type() const { return type_; }
|
||||
[[nodiscard]] uint64_t get_uid() const { return id; }
|
||||
[[nodiscard]] plugin_host_type get_type() const { return type_; }
|
||||
|
||||
virtual bool load_plugin(const char* path) = 0;
|
||||
|
||||
virtual void set_enabled(bool enabled) = 0;
|
||||
|
||||
[[nodiscard]] virtual bool is_enabled() const = 0;
|
||||
|
||||
virtual void on_update_sample_rate(double sample_rate) = 0;
|
||||
|
||||
virtual void on_update_buffer_size(int buffer_size);
|
||||
|
||||
[[nodiscard]] virtual uint32_t get_input_channels() const = 0;
|
||||
|
||||
[[nodiscard]] virtual uint32_t get_output_channels() const = 0;
|
||||
|
||||
virtual void update_channel_node_name() {}
|
||||
|
||||
virtual void process(uint32_t frame_num) = 0;
|
||||
|
||||
void try_open_editor();
|
||||
|
||||
void try_close_editor();
|
||||
|
||||
virtual void toggle_editor();
|
||||
|
||||
virtual void idle_editor() {}
|
||||
|
||||
void set_user_latency(int32_t latency) { user_latency_ = latency; }
|
||||
[[nodiscard]] auto get_user_latency() const -> int32_t { return user_latency_; }
|
||||
|
||||
[[nodiscard]] virtual bool has_editor() const { return false; }
|
||||
|
||||
virtual void get_editor_size(uint32_t& width, uint32_t& height) const = 0;
|
||||
|
||||
[[nodiscard]] virtual int32_t get_latency() const { return 0.0f; }
|
||||
|
||||
[[nodiscard]] virtual std::string load_name() const { return ""; }
|
||||
[[nodiscard]] virtual std::string load_vendor() const { return ""; }
|
||||
|
||||
[[nodiscard]] virtual std::string get_parameter_name(int index) const = 0;
|
||||
|
||||
[[nodiscard]] virtual std::string get_parameter_label(int index) const { return ""; };
|
||||
[[nodiscard]] virtual std::string get_parameter_display(int index) const { return ""; };
|
||||
|
||||
[[nodiscard]] virtual int32_t get_param_count() const = 0;
|
||||
[[nodiscard]] virtual bool is_param_index_valid(const int32_t index) const { return index >= 0 && index < get_param_count(); }
|
||||
|
||||
[[nodiscard]] virtual bool is_param_index_valid(const int32_t index) const {
|
||||
return index >= 0 && index < get_param_count();
|
||||
}
|
||||
|
||||
virtual void set_param_value(int32_t index, float value) = 0;
|
||||
|
||||
virtual float get_param_value(int32_t index) = 0;
|
||||
|
||||
virtual float get_param_min_value(int32_t index) { return 0.0f; }
|
||||
virtual float get_param_max_value(int32_t index) { return 1.0f; }
|
||||
|
||||
@ -81,16 +105,21 @@ public:
|
||||
[[nodiscard]] std::string get_imgui_id() const {
|
||||
return name + "##" + std::to_string(id);
|
||||
}
|
||||
|
||||
std::shared_ptr<circular_buffer_vector_type> ui_buffers;
|
||||
multicast_delegate<int32_t, float> on_param_changed; // int32_t param_index, float value
|
||||
multicast_delegate<int32_t> on_latency_changed; // int32_t latency_sample
|
||||
protected:
|
||||
void create_and_open_editor();
|
||||
|
||||
void destroy_editor();
|
||||
|
||||
virtual void on_open_editor(void* window_handle) = 0;
|
||||
|
||||
virtual void on_close_editor() = 0;
|
||||
|
||||
private:
|
||||
bool editor_opened = false;
|
||||
const uint64_t id = 0;
|
||||
plugin_host_type type_;
|
||||
plugin_host_type type_;
|
||||
int32_t user_latency_ = 0;
|
||||
};
|
||||
|
@ -24,14 +24,14 @@ void plugin_host_manager::release(singleton_release_guard& release_guard) {
|
||||
singleton_t<plugin_host_manager>::release(release_guard);
|
||||
release_guard.require_release<audio_device_manager>();
|
||||
|
||||
for (const plugin_host* host: plugin_hosts_) {
|
||||
free_plugin_host(host);
|
||||
for (plugin_host* host: plugin_hosts_) {
|
||||
delete host;
|
||||
}
|
||||
plugin_hosts_.clear();
|
||||
instrument_plugins_.clear();
|
||||
}
|
||||
|
||||
plugin_host *plugin_host_manager::create_effect_plugin_host(const char *path) {
|
||||
plugin_host* plugin_host_manager::create_effect_plugin_host(const char* path) {
|
||||
auto host = load_plugin(path);
|
||||
return host;
|
||||
}
|
||||
@ -61,7 +61,7 @@ void plugin_host_manager::remove_instrument_plugin_host(plugin_host* host) {
|
||||
}
|
||||
|
||||
plugin_host* plugin_host_manager::load_plugin(const char* path) {
|
||||
auto host = alloc_plugin_host<vst2_plugin_host>();
|
||||
auto host = new vst2_plugin_host();
|
||||
try {
|
||||
host->load_plugin(path);
|
||||
} catch (std::exception& e) {
|
||||
|
@ -3,49 +3,48 @@
|
||||
#include "misc/singleton/singleton.h"
|
||||
#include "taskflow/taskflow.hpp"
|
||||
#include "plugin_host.h"
|
||||
#include "mempool.h"
|
||||
#include "../../misc/mempool.h"
|
||||
#include <unordered_map>
|
||||
|
||||
class mixer_track;
|
||||
|
||||
class CORE_API plugin_host_manager : public singleton_t<plugin_host_manager> {
|
||||
public:
|
||||
void init(singleton_initliazer& initliazer) override;
|
||||
void begin_release(singleton_release_guard& release_guard) override;
|
||||
void release(singleton_release_guard& release_guard) override;
|
||||
void init(singleton_initliazer& initliazer) override;
|
||||
|
||||
plugin_host* create_effect_plugin_host(const char* path);
|
||||
plugin_host* create_instrument_plugin_host(const char* path);
|
||||
void remove_instrument_plugin_host(plugin_host* host);
|
||||
void begin_release(singleton_release_guard& release_guard) override;
|
||||
|
||||
const std::vector<plugin_host*>& get_plugin_hosts() { return plugin_hosts_; }
|
||||
const std::vector<plugin_host*>& get_instrument_hosts() { return instrument_plugins_; }
|
||||
void process(uint32_t in_frames);
|
||||
void release(singleton_release_guard& release_guard) override;
|
||||
|
||||
const char* get_name() override { return "plugin_host_manager"; }
|
||||
plugin_host* create_effect_plugin_host(const char* path);
|
||||
|
||||
plugin_host* create_instrument_plugin_host(const char* path);
|
||||
|
||||
void remove_instrument_plugin_host(plugin_host* host);
|
||||
|
||||
const std::vector<plugin_host*>& get_plugin_hosts() { return plugin_hosts_; }
|
||||
const std::vector<plugin_host*>& get_instrument_hosts() { return instrument_plugins_; }
|
||||
|
||||
void process(uint32_t in_frames);
|
||||
|
||||
const char* get_name() override { return "plugin_host_manager"; }
|
||||
|
||||
multicast_delegate<plugin_host*> on_instrument_added;
|
||||
multicast_delegate<plugin_host*> on_instrument_removed;
|
||||
|
||||
multicast_delegate<plugin_host*> on_instrument_added;
|
||||
multicast_delegate<plugin_host*> on_instrument_removed;
|
||||
private:
|
||||
plugin_host* load_plugin(const char* path);
|
||||
void register_instrument_plugin(plugin_host* host);
|
||||
void on_mixer_track_removed(mixer_track* track);
|
||||
plugin_host* load_plugin(const char* path);
|
||||
|
||||
template <typename T>
|
||||
T* alloc_plugin_host() {
|
||||
auto* host = plugin_host_map_[T::type].template alloc<T>();
|
||||
return host;
|
||||
}
|
||||
template <typename T>
|
||||
void free_plugin_host(T* host) {
|
||||
plugin_host_map_[host->get_type()].free(host);
|
||||
}
|
||||
std::vector<plugin_host*> instrument_plugins_{};
|
||||
std::vector<plugin_host*> plugin_hosts_{};
|
||||
std::unordered_map<plugin_host_type, mempool<>> plugin_host_map_;
|
||||
void register_instrument_plugin(plugin_host* host);
|
||||
|
||||
void update_taskflow(uint32_t in_frames);
|
||||
tf::Taskflow taskflow_;
|
||||
void on_mixer_track_removed(mixer_track* track);
|
||||
|
||||
std::vector<plugin_host*> instrument_plugins_{};
|
||||
std::vector<plugin_host*> plugin_hosts_{};
|
||||
|
||||
void update_taskflow(uint32_t in_frames);
|
||||
|
||||
tf::Taskflow taskflow_;
|
||||
};
|
||||
|
||||
DEFINE_SINGLETON_INSTANCE(plugin_host_manager)
|
||||
|
@ -2,31 +2,19 @@
|
||||
|
||||
#include "audio/device/audio_device_manager.h"
|
||||
#include "audio/mixer/channel_interface.h"
|
||||
#include "audio/mixer/latency_compensator.h"
|
||||
#include "window/window_manager.h"
|
||||
|
||||
std::unordered_map<std::string, std::weak_ptr<dynamic_library>> vst2_library_map;
|
||||
std::unordered_map<void*, uint32_t> vst2_latency_map;
|
||||
VstTimeInfo vst2_plugin_host::vst_time_info{};
|
||||
|
||||
std::unordered_map<std::string, bool> can_do_map =
|
||||
{
|
||||
{"sendVstEvents", true},
|
||||
{"sendVstMidiEvent", true},
|
||||
{"sendVstTimeInfo", true},
|
||||
{"receiveVstEvents", false},
|
||||
{"receiveVstMidiEvent", false},
|
||||
{"receiveVstTimeInfo", false},
|
||||
{"offline", false},
|
||||
{"reportConnectionChanges", false},
|
||||
{"acceptIOChanges", true},
|
||||
{"sizeWindow", true},
|
||||
{"asyncProcessing", true},
|
||||
{"supplyIdle", true},
|
||||
{"supportShell", false},
|
||||
{"openFileSelector", false},
|
||||
{"editFile", false},
|
||||
{"closeFileSelector", false},
|
||||
{"NIMKPIVendorSpecificCallbacks", false},
|
||||
std::unordered_map<std::string, bool> can_do_map = {
|
||||
{"sendVstEvents", true}, {"sendVstMidiEvent", true}, {"sendVstTimeInfo", true}, {"receiveVstEvents", false},
|
||||
{"receiveVstMidiEvent", false}, {"receiveVstTimeInfo", false}, {"offline", false},
|
||||
{"reportConnectionChanges", false}, {"acceptIOChanges", true}, {"sizeWindow", true}, {"asyncProcessing", true},
|
||||
{"supplyIdle", true}, {"supportShell", false}, {"openFileSelector", false}, {"editFile", false},
|
||||
{"closeFileSelector", false}, {"NIMKPIVendorSpecificCallbacks", false},
|
||||
};
|
||||
|
||||
std::shared_ptr<dynamic_library> get_vst2_library(const std::string& path) {
|
||||
@ -38,109 +26,98 @@ std::shared_ptr<dynamic_library> get_vst2_library(const std::string& path) {
|
||||
return library;
|
||||
}
|
||||
|
||||
bool vst_host_can_do(const char* can_do)
|
||||
{
|
||||
if (can_do_map.contains(can_do))
|
||||
return can_do_map[can_do];
|
||||
bool vst_host_can_do(const char* can_do) {
|
||||
if (can_do_map.contains(can_do)) return can_do_map[can_do];
|
||||
spdlog::warn("Unknown can do: {}", can_do);
|
||||
return false;
|
||||
}
|
||||
|
||||
VstIntPtr vst_master_callback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case audioMasterAutomate: {
|
||||
auto host = ((vst2_plugin_host *) effect->user);
|
||||
VstIntPtr vst_master_callback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) {
|
||||
switch (opcode) {
|
||||
case audioMasterAutomate : {
|
||||
auto host = ((vst2_plugin_host*) effect->user);
|
||||
host->on_param_changed.broadcast(index, opt);
|
||||
#if BUILD_DEBUG
|
||||
#if BUILD_DEBUG
|
||||
spdlog::info("vst2 plugin {} automate, {} index = {}, value = {}", host->name, host->get_parameter_name(index), index, opt);
|
||||
#endif
|
||||
#endif
|
||||
auto old_latency = vst2_latency_map[host];
|
||||
auto new_latency = host->get_latency();
|
||||
if (old_latency != new_latency) {
|
||||
vst2_latency_map[host] = new_latency;
|
||||
host->on_latency_changed.broadcast(new_latency);
|
||||
#if BUILD_DEBUG
|
||||
on_latency_offset_changed.broadcast();
|
||||
#if BUILD_DEBUG
|
||||
spdlog::info("vst2 plugin {} latency changed: {}", host->name, new_latency);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
case audioMasterVersion:
|
||||
case audioMasterVersion :
|
||||
return kVstVersion; // 返回插件版本号
|
||||
case audioMasterWantMidi:
|
||||
case audioMasterWantMidi :
|
||||
return 1;
|
||||
case audioMasterGetSampleRate:
|
||||
case audioMasterGetSampleRate :
|
||||
return g_audio_device_manager.get_sample_rate(); // 返回采样率
|
||||
case audioMasterGetCurrentProcessLevel:
|
||||
case audioMasterGetCurrentProcessLevel :
|
||||
return kVstProcessLevelRealtime; // 返回当前处理级别
|
||||
case audioMasterGetBlockSize:
|
||||
case audioMasterGetBlockSize :
|
||||
return g_audio_device_manager.get_buffer_size(); // 返回块大小
|
||||
case audioMasterSizeWindow:
|
||||
{
|
||||
case audioMasterSizeWindow : {
|
||||
// 设置插件窗口大小
|
||||
g_window_manager.resize_plugin_window((plugin_host*)effect->user, index, value);
|
||||
g_window_manager.resize_plugin_window((plugin_host*) effect->user, index, value);
|
||||
return 1;
|
||||
}
|
||||
case audioMasterGetTime:
|
||||
return (VstIntPtr)&vst2_time;
|
||||
case audioMasterIdle:
|
||||
case audioMasterGetTime :
|
||||
return (VstIntPtr) &vst2_time;
|
||||
case audioMasterIdle :
|
||||
// UE_LOG(LogTemp, Log, TEXT("Plugin Idle"));
|
||||
return 1;
|
||||
case audioMasterNeedIdle:
|
||||
return 1;
|
||||
case audioMasterNeedIdle :
|
||||
// UE_LOG(LogTemp, Log, TEXT("Plugin Need Idle"));
|
||||
return 1;
|
||||
case audioMasterGetVendorString:
|
||||
{
|
||||
const char* Ptr1 = (const char*)ptr;
|
||||
return 1;
|
||||
case audioMasterGetVendorString : {
|
||||
const char* Ptr1 = (const char*) ptr;
|
||||
Ptr1 = "Arona";
|
||||
return 1;
|
||||
}
|
||||
case audioMasterGetProductString:
|
||||
{
|
||||
const char* Ptr1 = (const char*)ptr;
|
||||
case audioMasterGetProductString : {
|
||||
const char* Ptr1 = (const char*) ptr;
|
||||
Ptr1 = "Arona";
|
||||
return 1;
|
||||
}
|
||||
case audioMasterGetVendorVersion:
|
||||
case audioMasterGetVendorVersion :
|
||||
return 1000;
|
||||
case audioMasterCanDo:
|
||||
{
|
||||
const char* Ptr1 = (const char*)ptr;
|
||||
case audioMasterCanDo : {
|
||||
const char* Ptr1 = (const char*) ptr;
|
||||
return vst_host_can_do(Ptr1) ? 1 : 0;
|
||||
}
|
||||
case audioMasterUpdateDisplay:
|
||||
case audioMasterUpdateDisplay :
|
||||
return 1;
|
||||
case audioMasterBeginEdit:
|
||||
{
|
||||
case audioMasterBeginEdit : {
|
||||
// UE_LOG(LogTemp, Log, TEXT("Want Begin Edit"));
|
||||
return 1;
|
||||
}
|
||||
case audioMasterEndEdit:
|
||||
{
|
||||
case audioMasterEndEdit : {
|
||||
// UE_LOG(LogTemp, Log, TEXT("Want End Edit"));
|
||||
return 1;
|
||||
}
|
||||
case audioMasterVendorSpecific:
|
||||
case audioMasterVendorSpecific :
|
||||
return 0;
|
||||
case audioMasterProcessEvents:
|
||||
case audioMasterProcessEvents :
|
||||
// TODO
|
||||
{
|
||||
// VstEvents* Events = (VstEvents*)ptr;
|
||||
// Events->events[0]->type;
|
||||
// FVST2PluginHost* Host = static_cast<FVST2PluginHost*>(Effect->user);
|
||||
|
||||
}
|
||||
case audioMasterGetInputLatency:
|
||||
case audioMasterGetInputLatency :
|
||||
spdlog::info("Get Input Latency: {}", value);
|
||||
return 0;
|
||||
case audioMasterGetOutputLatency:
|
||||
case audioMasterGetOutputLatency :
|
||||
spdlog::info("Get Output Latency: {}", value);
|
||||
return 0;
|
||||
return 1;
|
||||
default: break;
|
||||
// ensureMsgf(0, TEXT("No Implement OpCode: %d"), OpCode);
|
||||
return 1;
|
||||
default : break;
|
||||
// ensureMsgf(0, TEXT("No Implement OpCode: %d"), OpCode);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -165,7 +142,7 @@ bool vst2_plugin_host::load_plugin(const char* path) {
|
||||
// load vst
|
||||
library_ = get_vst2_library(path);
|
||||
// get main function
|
||||
const auto entry_proc = (vst_plugin_entry_proc)(library_->get_function("VSTPluginMain"));
|
||||
const auto entry_proc = (vst_plugin_entry_proc) (library_->get_function("VSTPluginMain"));
|
||||
if (!entry_proc) {
|
||||
spdlog::error("Failed to get entry point from {}", path);
|
||||
return false;
|
||||
@ -175,8 +152,7 @@ bool vst2_plugin_host::load_plugin(const char* path) {
|
||||
if (!effect_) {
|
||||
return false;
|
||||
}
|
||||
if (effect_->magic != kEffectMagic)
|
||||
throw std::runtime_error("Not a VST2 plugin");
|
||||
if (effect_->magic != kEffectMagic) throw std::runtime_error("Not a VST2 plugin");
|
||||
effect_->user = this;
|
||||
|
||||
name = load_name();
|
||||
@ -213,20 +189,16 @@ uint32_t vst2_plugin_host::get_output_channels() const {
|
||||
}
|
||||
|
||||
void vst2_plugin_host::update_channel_node_name() {
|
||||
for (int i = 0; i < effect_->numInputs; ++i)
|
||||
{
|
||||
for (int i = 0; i < effect_->numInputs; ++i) {
|
||||
VstPinProperties pin_properties{};
|
||||
if (dispatch(effGetInputProperties, i, 0, &pin_properties) == 1)
|
||||
{
|
||||
if (dispatch(effGetInputProperties, i, 0, &pin_properties) == 1) {
|
||||
channel->set_input_channel_node_name(i / 2, i, pin_properties.label);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < effect_->numOutputs; ++i)
|
||||
{
|
||||
for (int i = 0; i < effect_->numOutputs; ++i) {
|
||||
VstPinProperties pin_properties{};
|
||||
if (dispatch(effGetOutputProperties, i, 0, &pin_properties) == 1)
|
||||
{
|
||||
if (dispatch(effGetOutputProperties, i, 0, &pin_properties) == 1) {
|
||||
channel->set_output_channel_node_name(i / 2, i, pin_properties.label);
|
||||
}
|
||||
}
|
||||
@ -234,14 +206,14 @@ void vst2_plugin_host::update_channel_node_name() {
|
||||
|
||||
void vst2_plugin_host::process(uint32_t frame_num) {
|
||||
// TODO send midi
|
||||
#if USE_DOUBLE_SAMPLE
|
||||
#if USE_DOUBLE_SAMPLE
|
||||
effect_->processDoubleReplacing(effect_, channel->get_input_headers(), channel->get_output_headers(), static_cast<VstInt32>(frame_num));
|
||||
#else
|
||||
effect_->processReplacing(effect_, channel->get_input_headers(), channel->get_output_headers(), static_cast<VstInt32>(frame_num));
|
||||
#endif
|
||||
#else
|
||||
effect_->processReplacing(effect_, channel->get_input_headers(), channel->get_output_headers(),
|
||||
static_cast<VstInt32>(frame_num));
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < ui_buffers->size(); ++i)
|
||||
{
|
||||
for (int i = 0; i < ui_buffers->size(); ++i) {
|
||||
ui_buffers->at(i).push(channel->get_output_headers()[i], frame_num);
|
||||
}
|
||||
}
|
||||
@ -283,14 +255,12 @@ std::string vst2_plugin_host::load_vendor() const {
|
||||
}
|
||||
|
||||
void vst2_plugin_host::on_open_editor(void* window_handle) {
|
||||
if (!has_editor())
|
||||
return;
|
||||
if (!has_editor()) return;
|
||||
dispatch(effEditOpen, 0, 0, window_handle);
|
||||
}
|
||||
|
||||
void vst2_plugin_host::on_close_editor() {
|
||||
if (!has_editor())
|
||||
return;
|
||||
if (!has_editor()) return;
|
||||
dispatch(effEditClose);
|
||||
}
|
||||
|
||||
@ -316,46 +286,34 @@ std::string vst2_plugin_host::get_parameter_display(int index) const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int32_t vst2_plugin_host::get_param_count() const
|
||||
{
|
||||
if (!effect_)
|
||||
return 0;
|
||||
int32_t vst2_plugin_host::get_param_count() const {
|
||||
if (!effect_) return 0;
|
||||
return effect_->numParams;
|
||||
}
|
||||
|
||||
void vst2_plugin_host::set_param_value(int32_t index, float value)
|
||||
{
|
||||
if (effect_)
|
||||
effect_->setParameter(effect_, index, value);
|
||||
void vst2_plugin_host::set_param_value(int32_t index, float value) {
|
||||
if (effect_) effect_->setParameter(effect_, index, value);
|
||||
}
|
||||
|
||||
float vst2_plugin_host::get_param_value(int32_t index)
|
||||
{
|
||||
if (!effect_)
|
||||
return 0;
|
||||
float vst2_plugin_host::get_param_value(int32_t index) {
|
||||
if (!effect_) return 0;
|
||||
return effect_->getParameter(effect_, index);
|
||||
}
|
||||
|
||||
float vst2_plugin_host::get_param_min_value(int32_t index) {
|
||||
VstParameterProperties param_properties{};
|
||||
if (dispatch(effGetParameterProperties, index, 0, ¶m_properties) == 1)
|
||||
{
|
||||
if (param_properties.flags & kVstParameterUsesIntegerMinMax)
|
||||
return param_properties.minInteger;
|
||||
if (param_properties.flags & kVstParameterUsesFloatStep)
|
||||
return param_properties.smallStepFloat;
|
||||
if (dispatch(effGetParameterProperties, index, 0, ¶m_properties) == 1) {
|
||||
if (param_properties.flags & kVstParameterUsesIntegerMinMax) return param_properties.minInteger;
|
||||
if (param_properties.flags & kVstParameterUsesFloatStep) return param_properties.smallStepFloat;
|
||||
}
|
||||
return plugin_host::get_param_min_value(index);
|
||||
}
|
||||
|
||||
float vst2_plugin_host::get_param_max_value(int32_t index) {
|
||||
VstParameterProperties param_properties{};
|
||||
if (dispatch(effGetParameterProperties, index, 0, ¶m_properties) == 1)
|
||||
{
|
||||
if (param_properties.flags & kVstParameterUsesIntegerMinMax)
|
||||
return param_properties.maxInteger;
|
||||
if (param_properties.flags & kVstParameterUsesFloatStep)
|
||||
return param_properties.largeStepFloat;
|
||||
if (dispatch(effGetParameterProperties, index, 0, ¶m_properties) == 1) {
|
||||
if (param_properties.flags & kVstParameterUsesIntegerMinMax) return param_properties.maxInteger;
|
||||
if (param_properties.flags & kVstParameterUsesFloatStep) return param_properties.largeStepFloat;
|
||||
}
|
||||
return plugin_host::get_param_max_value(index);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "pluginterfaces/vst2.x/aeffect.h"
|
||||
#include "pluginterfaces/vst2.x/aeffectx.h"
|
||||
|
||||
class vst2_plugin_host : public plugin_host {
|
||||
class vst2_plugin_host : public plugin_host, public pool_obj<vst2_plugin_host> {
|
||||
public:
|
||||
static VstTimeInfo vst_time_info;
|
||||
static constexpr auto type = plugin_host_type::vst2;
|
||||
|
121
core/misc/mempool.h
Normal file
121
core/misc/mempool.h
Normal file
@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class memory_pool {
|
||||
public:
|
||||
memory_pool(const size_t chunk_size, const size_t chunks_per_block)
|
||||
: chunk_size_(chunk_size), chunks_per_block_(chunks_per_block), current_block_(nullptr), free_list_(nullptr) {
|
||||
allocate_block();
|
||||
}
|
||||
memory_pool(memory_pool&& other) noexcept
|
||||
: chunk_size_(other.chunk_size_), chunks_per_block_(other.chunks_per_block_), blocks_(std::move(other.blocks_)),
|
||||
current_block_(other.current_block_), free_list_(other.free_list_) {
|
||||
other.current_block_ = nullptr;
|
||||
other.free_list_ = nullptr;
|
||||
}
|
||||
|
||||
~memory_pool() {
|
||||
for (void* block : blocks_) {
|
||||
delete[] static_cast<char*>(block);
|
||||
}
|
||||
}
|
||||
|
||||
auto allocate() -> void* {
|
||||
if (!free_list_) {
|
||||
allocate_block();
|
||||
}
|
||||
|
||||
std::lock_guard lock(mutex_);
|
||||
void* chunk = free_list_;
|
||||
free_list_ = *static_cast<void**>(free_list_);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void deallocate(void* chunk) {
|
||||
std::lock_guard lock(mutex_);
|
||||
*static_cast<void**>(chunk) = free_list_;
|
||||
free_list_ = chunk;
|
||||
}
|
||||
|
||||
private:
|
||||
void allocate_block() {
|
||||
const size_t block_size = chunk_size_ * chunks_per_block_;
|
||||
const auto new_block = new char[block_size];
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
blocks_.push_back(new_block);
|
||||
current_block_ = new_block;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < chunks_per_block_; ++i) {
|
||||
void* chunk = new_block + i * chunk_size_;
|
||||
deallocate(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
size_t chunk_size_;
|
||||
size_t chunks_per_block_;
|
||||
std::vector<void*> blocks_;
|
||||
void* current_block_;
|
||||
void* free_list_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class obj_mempool {
|
||||
public:
|
||||
template<typename ...Args>
|
||||
static auto alloc(Args&&... args) -> T* {
|
||||
auto obj = static_cast<T*>(pool_.allocate());
|
||||
new (obj) T(std::forward<Args>(args)...);
|
||||
objs_.push_back(obj);
|
||||
return obj;
|
||||
}
|
||||
static auto alloc() -> T* {
|
||||
auto obj = static_cast<T*>(pool_.allocate());
|
||||
new (obj) T();
|
||||
objs_.push_back(obj);
|
||||
return obj;
|
||||
}
|
||||
static void free(T* p) {
|
||||
p->~T();
|
||||
pool_.deallocate(p);
|
||||
objs_.erase(std::remove(objs_.begin(), objs_.end(), p), objs_.end());
|
||||
}
|
||||
static void free_all() {
|
||||
for (auto obj : objs_) {
|
||||
obj->~T();
|
||||
pool_.deallocate(obj);
|
||||
}
|
||||
objs_.clear();
|
||||
}
|
||||
static auto objs() -> const std::vector<T*>& {
|
||||
return objs_;
|
||||
}
|
||||
static auto has_obj(T* p) -> bool {
|
||||
return std::find(objs_.begin(), objs_.end(), p) != objs_.end();
|
||||
}
|
||||
static auto safe_free(T* p) -> bool {
|
||||
if (has_obj(p)) {
|
||||
free(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
inline static memory_pool pool_ = memory_pool(sizeof(T), 64);
|
||||
inline static std::vector<T*> objs_;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class pool_obj {
|
||||
inline static memory_pool pool = memory_pool(sizeof(T), 64);
|
||||
public:
|
||||
auto operator new(size_t size) -> void* {
|
||||
return pool.allocate();
|
||||
}
|
||||
void operator delete(void* p) {
|
||||
pool.deallocate(p);
|
||||
}
|
||||
};
|
@ -4,9 +4,9 @@
|
||||
|
||||
void thread_message_hub::process_messages() {
|
||||
while (!messages_.empty()) {
|
||||
thread_message* message = messages_.front();
|
||||
lamba_thread_message* message = messages_.front();
|
||||
message->execute();
|
||||
mem_pool_.free(message);
|
||||
obj_mempool<lamba_thread_message>::free(message);
|
||||
messages_.pop();
|
||||
}
|
||||
}
|
||||
|
@ -2,30 +2,29 @@
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "mempool.h"
|
||||
#include "../misc/mempool.h"
|
||||
#include "thread_message.h"
|
||||
|
||||
class CORE_API thread_message_hub {
|
||||
public:
|
||||
template<typename T, typename ...Args>
|
||||
void push_message(Args&&... args) {
|
||||
T* message = mem_pool_.alloc<T>(std::forward<Args>(args)...);
|
||||
push_message(message);
|
||||
}
|
||||
template<typename T>
|
||||
void push_message() {
|
||||
T* message = mem_pool_.alloc<T>();
|
||||
push_message(message);
|
||||
}
|
||||
// template<typename T, typename ...Args>
|
||||
// void push_message(Args&&... args) {
|
||||
// T* message = obj_mempool<T>::alloc(std::forward<Args>(args)...);
|
||||
// push_message(message);
|
||||
// }
|
||||
// template<typename T>
|
||||
// void push_message() {
|
||||
// T* message = obj_mempool<T>::alloc();
|
||||
// push_message(message);
|
||||
// }
|
||||
void push_message(const std::function<void()>& func) {
|
||||
auto* message = mem_pool_.alloc<lamba_thread_message>(func);
|
||||
auto* message = obj_mempool<lamba_thread_message>::alloc(func);
|
||||
push_message(message);
|
||||
}
|
||||
void process_messages();
|
||||
private:
|
||||
void push_message(thread_message* message) {
|
||||
void push_message(lamba_thread_message* message) {
|
||||
messages_.push(message);
|
||||
}
|
||||
std::queue<thread_message*> messages_;
|
||||
mempool<> mem_pool_ = mempool(1024000);
|
||||
std::queue<lamba_thread_message*> messages_;
|
||||
};
|
||||
|
7
third_party/mempool/CMakeLists.txt
vendored
7
third_party/mempool/CMakeLists.txt
vendored
@ -5,12 +5,7 @@ set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CPP_STANDARD 17)
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED
|
||||
mempool/ncx_core.h
|
||||
mempool/ncx_lock.h
|
||||
mempool/ncx_slab.c
|
||||
mempool/ncx_slab.h
|
||||
mempool/my_unistd.h
|
||||
mempool.h
|
||||
../../core/misc/mempool.h
|
||||
extern.h
|
||||
)
|
||||
include_directories(mempool)
|
||||
|
96
third_party/mempool/mempool.h
vendored
96
third_party/mempool/mempool.h
vendored
@ -1,96 +0,0 @@
|
||||
#pragma once
|
||||
#include "ncx_slab.h"
|
||||
#include <cstdlib>
|
||||
|
||||
template<int32_t alignment = 64>
|
||||
class mempool {
|
||||
public:
|
||||
mempool(size_t pool_size = 1024000) { // 1024KB
|
||||
const auto space = static_cast<u_char*>(malloc(pool_size));
|
||||
|
||||
mem_pool_ = reinterpret_cast<ncx_slab_pool_t*>(space);
|
||||
mem_pool_->addr = space;
|
||||
mem_pool_->min_shift = 3;
|
||||
mem_pool_->end = space + pool_size;
|
||||
|
||||
ncx_slab_init(mem_pool_);
|
||||
}
|
||||
~mempool() {
|
||||
free(mem_pool_->addr);
|
||||
}
|
||||
[[nodiscard]] void* alloc(size_t size) const {
|
||||
return ncx_slab_alloc(mem_pool_, size);
|
||||
}
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
[[nodiscard]] T* alloc(Args&&... args) const {
|
||||
T* out = (T*)alloc(sizeof(T));
|
||||
new (out) T(std::forward<Args>(args)...);
|
||||
return out;
|
||||
}
|
||||
template<typename T>
|
||||
[[nodiscard]] T* alloc() const {
|
||||
T* out = (T*)alloc(sizeof(T));
|
||||
new (out) T();
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void free(T* p) const {
|
||||
p->~T();
|
||||
free((void*)p);
|
||||
}
|
||||
|
||||
void free(void* p) const {
|
||||
ncx_slab_free(mem_pool_, p);
|
||||
}
|
||||
[[nodiscard]] ncx_slab_stat_t stat() const {
|
||||
ncx_slab_stat_t out;
|
||||
ncx_slab_stat(mem_pool_, &out);
|
||||
return out;
|
||||
}
|
||||
private:
|
||||
ncx_slab_pool_t* mem_pool_ = nullptr;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class obj_mempool {
|
||||
public:
|
||||
template<typename ...Args>
|
||||
static T* alloc(Args&&... args) {
|
||||
auto obj = pool_.template alloc<T>(std::forward<Args>(args)...);
|
||||
objs_.push_back(obj);
|
||||
return obj;
|
||||
}
|
||||
static T* alloc() {
|
||||
auto obj = pool_.template alloc<T>();
|
||||
objs_.push_back(obj);
|
||||
return obj;
|
||||
}
|
||||
static void free(T* p) {
|
||||
pool_.template free<T>(p);
|
||||
objs_.erase(std::remove(objs_.begin(), objs_.end(), p), objs_.end());
|
||||
}
|
||||
static void free_all() {
|
||||
for (auto obj : objs_) {
|
||||
pool_.template free<T>(obj);
|
||||
}
|
||||
objs_.clear();
|
||||
}
|
||||
static const std::vector<T*>& objs() {
|
||||
return objs_;
|
||||
}
|
||||
static bool has_obj(T* p) {
|
||||
return std::find(objs_.begin(), objs_.end(), p) != objs_.end();
|
||||
}
|
||||
static bool safe_free(T* p) {
|
||||
if (has_obj(p)) {
|
||||
free(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
static mempool<alignof(T)> pool_;
|
||||
static std::vector<T*> objs_;
|
||||
};
|
41
third_party/mempool/mempool/Makefile
vendored
41
third_party/mempool/mempool/Makefile
vendored
@ -1,41 +0,0 @@
|
||||
CC=gcc
|
||||
CXX=g++
|
||||
INC+=
|
||||
LIB+=
|
||||
|
||||
#判断系统架构(32bit, 64bit)
|
||||
ARCH := $(shell getconf LONG_BIT)
|
||||
ifeq ($(ARCH),64)
|
||||
CFLAGS+= -DNCX_PTR_SIZE=8
|
||||
else
|
||||
CFLAGS+= -DNCX_PTR_SIZE=4
|
||||
endif
|
||||
|
||||
#CFLAGS+= -pipe -O0 -Wall -g3 -ggdb3
|
||||
CFLAGS+= -pipe -O3
|
||||
#定义是否打印日志
|
||||
CFLAGS+= -DLOG_LEVEL=4
|
||||
#是否与malloc类似模拟脏数据
|
||||
#CFLAGS+= -DNCX_DEBUG_MALLOC
|
||||
#是否自动合并碎片
|
||||
CFLAGS+= -DPAGE_MERGE
|
||||
|
||||
TARGET=pool_test
|
||||
ALL:$(TARGET)
|
||||
|
||||
OBJ= ncx_slab.o
|
||||
|
||||
$(TARGET):$(OBJ) main.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LIB)
|
||||
|
||||
pool_bench:$(OBJ) bench.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LIB)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) $(INC) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f $(TARGET) pool_bench
|
||||
|
||||
install:
|
106
third_party/mempool/mempool/README.md
vendored
106
third_party/mempool/mempool/README.md
vendored
@ -1,106 +0,0 @@
|
||||
ncx_mempool
|
||||
======================
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
a nginx-based memory pool . <br/>
|
||||
both share-memory and private-memory are supported. it is more efficient than malloc.<br/>
|
||||
it is a very light weight and do not rely on any third-party library.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
1 #include "ncx_slab.h"
|
||||
2
|
||||
3 int main(int argc, char **argv)
|
||||
4 {
|
||||
5 char *p;
|
||||
6 size_t pool_size = 4096000; //4M
|
||||
7 ncx_slab_stat_t stat;
|
||||
8 u_char *space;
|
||||
9 space = (u_char *)malloc(pool_size);
|
||||
10
|
||||
11 ncx_slab_pool_t *sp;
|
||||
12 sp = (ncx_slab_pool_t*) space;
|
||||
13
|
||||
14 sp->addr = space;
|
||||
15 sp->min_shift = 3;
|
||||
16 sp->end = space + pool_size;
|
||||
17
|
||||
18 /*
|
||||
19 * init
|
||||
20 */
|
||||
21 ncx_slab_init(sp);
|
||||
22
|
||||
23 /*
|
||||
24 * alloc
|
||||
25 */
|
||||
26 p = ncx_slab_alloc(sp, 128);
|
||||
27
|
||||
28 /*
|
||||
29 * memory usage api.
|
||||
30 */
|
||||
31 ncx_slab_stat(sp, &stat);
|
||||
32
|
||||
33 /*
|
||||
34 * free
|
||||
35 */
|
||||
36 ncx_slab_free(sp, p);
|
||||
37
|
||||
38 free(space);
|
||||
39
|
||||
40 return 0;
|
||||
41}
|
||||
|
||||
API
|
||||
===
|
||||
**ncx_slab_init(ncx_slab_pool_t *pool)** <br/>
|
||||
**Description**: 初始化内存池结构;
|
||||
|
||||
**ncx_slab_alloc(ncx_slab_pool_t *pool, size_t size)**<br/>
|
||||
**Description**: 内存分配
|
||||
|
||||
**ncx_slab_free(ncx_slab_pool_t *pool, void *p)** <br/>
|
||||
**Description**: 释放内存
|
||||
|
||||
**ncx_slab_stat(ncx_slab_pool_t *pool, ncx_slab_stat_t *stat)**<br/>
|
||||
**Description**: 查看内存池使用情况
|
||||
|
||||
Customization
|
||||
=============
|
||||
正如example所示,内存池内存是由应用层先分配,ncx_mempool是在给定的内存基础上进行分配和回收管理。 <br/>
|
||||
所以内存池本身不关心所管理的内存是私有内存 or 共享内存;
|
||||
|
||||
ncx_lock.h 是锁接口;根据实际需要重定义: <br/>
|
||||
1.多线程共享内存池,可参考pthread_spin_lock <br/>
|
||||
2.多进程共享内存池,可参考nginx的ngx_shmtx.c实现spin lock <br/>
|
||||
3.单进程单线程使用内存池,无锁编程..
|
||||
|
||||
ncx_log.h 是日志接口,根据实际需要重定义.
|
||||
|
||||
ncx_slab.c.orz 是 ncx_slab.c的详细注释,方便理解.
|
||||
|
||||
Author
|
||||
======
|
||||
dcshi(施俊伟) <dcshi@qq.com>
|
||||
|
||||
Copyright and License
|
||||
=====================
|
||||
This module is licensed under the BSD license.
|
||||
Copyright (C) 2013, by dcshi(施俊伟). <dcshi@qq.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
|
||||
the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
79
third_party/mempool/mempool/bench.c
vendored
79
third_party/mempool/mempool/bench.c
vendored
@ -1,79 +0,0 @@
|
||||
#include "ncx_slab.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
uint64_t usTime()
|
||||
{
|
||||
struct timeval tv;
|
||||
uint64_t usec;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
usec = ((uint64_t)tv.tv_sec)*1000000LL;
|
||||
usec += tv.tv_usec;
|
||||
|
||||
return usec;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ncx_slab_pool_t *sp;
|
||||
size_t pool_size;
|
||||
u_char *space;
|
||||
|
||||
pool_size = 4096000; //4M
|
||||
space = (u_char *)malloc(pool_size);
|
||||
sp = (ncx_slab_pool_t*) space;
|
||||
|
||||
sp->addr = space;
|
||||
sp->min_shift = 3;
|
||||
sp->end = space + pool_size;
|
||||
|
||||
ncx_slab_init(sp);
|
||||
|
||||
char *p;
|
||||
uint64_t us_begin;
|
||||
uint64_t us_end;
|
||||
size_t size[] = { 30, 120, 256, 500, 1000, 2000, 3000, 5000};
|
||||
|
||||
printf("size\tncx\tmalloc\tpct\n");
|
||||
|
||||
int i, j;
|
||||
uint64_t t1, t2;
|
||||
for (j = 0; j < sizeof(size)/sizeof(size_t); j++)
|
||||
{
|
||||
size_t s = size[j];
|
||||
printf("%d\t", s);
|
||||
|
||||
//test for ncx_pool
|
||||
us_begin = usTime();
|
||||
for(i = 0; i < 1000000; i++)
|
||||
{
|
||||
p = ncx_slab_alloc(sp, s);
|
||||
|
||||
ncx_slab_free(sp, p);
|
||||
}
|
||||
us_end = usTime();
|
||||
|
||||
t1 = (us_end - us_begin);
|
||||
printf("%llu \t", t1/1000);
|
||||
|
||||
// test for malloc
|
||||
us_begin = usTime();
|
||||
for(i = 0; i < 1000000; i++)
|
||||
{
|
||||
p = (char*)malloc(s);
|
||||
|
||||
free(p);
|
||||
}
|
||||
us_end = usTime();
|
||||
|
||||
t2 = (us_end - us_begin);
|
||||
printf("%llu\t", t2/1000);
|
||||
|
||||
printf("%.2f\n", (double)t1 / (double)t2);
|
||||
}
|
||||
|
||||
free(space);
|
||||
|
||||
return 0;
|
||||
}
|
53
third_party/mempool/mempool/main.c
vendored
53
third_party/mempool/mempool/main.c
vendored
@ -1,53 +0,0 @@
|
||||
#include "ncx_slab.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *p;
|
||||
size_t pool_size = 4096000; //4M
|
||||
ncx_slab_stat_t stat;
|
||||
u_char *space;
|
||||
space = (u_char *)malloc(pool_size);
|
||||
|
||||
ncx_slab_pool_t *sp;
|
||||
sp = (ncx_slab_pool_t*) space;
|
||||
|
||||
sp->addr = space;
|
||||
sp->min_shift = 3;
|
||||
sp->end = space + pool_size;
|
||||
|
||||
ncx_slab_init(sp);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 1000000; i++)
|
||||
{
|
||||
p = ncx_slab_alloc(sp, 128 + i);
|
||||
if (p == NULL)
|
||||
{
|
||||
printf("%d\n", i);
|
||||
return -1;
|
||||
}
|
||||
ncx_slab_free(sp, p);
|
||||
}
|
||||
ncx_slab_stat(sp, &stat);
|
||||
|
||||
printf("##########################################################################\n");
|
||||
for (i = 0; i < 2500; i++)
|
||||
{
|
||||
p = ncx_slab_alloc(sp, 30 + i);
|
||||
if (p == NULL)
|
||||
{
|
||||
printf("%d\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (i % 3 == 0)
|
||||
{
|
||||
ncx_slab_free(sp, p);
|
||||
}
|
||||
}
|
||||
ncx_slab_stat(sp, &stat);
|
||||
|
||||
free(space);
|
||||
|
||||
return 0;
|
||||
}
|
12
third_party/mempool/mempool/my_unistd.h
vendored
12
third_party/mempool/mempool/my_unistd.h
vendored
@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
inline uintptr_t getpagesize() {
|
||||
SYSTEM_INFO systemInfo;
|
||||
GetSystemInfo(&systemInfo);
|
||||
return systemInfo.dwPageSize;
|
||||
}
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
25
third_party/mempool/mempool/ncx_core.h
vendored
25
third_party/mempool/mempool/ncx_core.h
vendored
@ -1,25 +0,0 @@
|
||||
#ifndef _NCX_CORE_H_
|
||||
#define _NCX_CORE_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef unsigned char u_char;
|
||||
typedef uintptr_t ncx_uint_t;
|
||||
typedef intptr_t ncx_int_t;
|
||||
|
||||
#ifndef NCX_ALIGNMENT
|
||||
#define NCX_ALIGNMENT sizeof(unsigned long) /* platform word */
|
||||
#endif
|
||||
|
||||
#define ncx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
|
||||
#define ncx_align_ptr(p, a) \
|
||||
(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
|
||||
|
||||
#define ncx_memzero(buf, n) (void) memset(buf, 0, n)
|
||||
#define ncx_memset(buf, c, n) (void) memset(buf, c, n)
|
||||
|
||||
#endif
|
13
third_party/mempool/mempool/ncx_lock.h
vendored
13
third_party/mempool/mempool/ncx_lock.h
vendored
@ -1,13 +0,0 @@
|
||||
#ifndef _NCX_LOCK_H_
|
||||
#define _NCX_LOCK_H_
|
||||
|
||||
#define ncx_shmtx_lock(x) { /*void*/ }
|
||||
#define ncx_shmtx_unlock(x) { /*void*/ }
|
||||
|
||||
typedef struct {
|
||||
|
||||
ncx_uint_t spin;
|
||||
|
||||
} ncx_shmtx_t;
|
||||
|
||||
#endif
|
848
third_party/mempool/mempool/ncx_slab.c
vendored
848
third_party/mempool/mempool/ncx_slab.c
vendored
@ -1,848 +0,0 @@
|
||||
#include "ncx_slab.h"
|
||||
#include <my_unistd.h>
|
||||
|
||||
#define NCX_SLAB_PAGE_MASK 3
|
||||
#define NCX_SLAB_PAGE 0
|
||||
#define NCX_SLAB_BIG 1
|
||||
#define NCX_SLAB_EXACT 2
|
||||
#define NCX_SLAB_SMALL 3
|
||||
|
||||
#if (NCX_PTR_SIZE == 4)
|
||||
|
||||
#define NCX_SLAB_PAGE_FREE 0
|
||||
#define NCX_SLAB_PAGE_BUSY 0xffffffff
|
||||
#define NCX_SLAB_PAGE_START 0x80000000
|
||||
|
||||
#define NCX_SLAB_SHIFT_MASK 0x0000000f
|
||||
#define NCX_SLAB_MAP_MASK 0xffff0000
|
||||
#define NCX_SLAB_MAP_SHIFT 16
|
||||
|
||||
#define NCX_SLAB_BUSY 0xffffffff
|
||||
|
||||
#else /* (NCX_PTR_SIZE == 8) */
|
||||
|
||||
#define NCX_SLAB_PAGE_FREE 0
|
||||
#define NCX_SLAB_PAGE_BUSY 0xffffffffffffffff
|
||||
#define NCX_SLAB_PAGE_START 0x8000000000000000
|
||||
|
||||
#define NCX_SLAB_SHIFT_MASK 0x000000000000000f
|
||||
#define NCX_SLAB_MAP_MASK 0xffffffff00000000
|
||||
#define NCX_SLAB_MAP_SHIFT 32
|
||||
|
||||
#define NCX_SLAB_BUSY 0xffffffffffffffff
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if (NCX_DEBUG_MALLOC)
|
||||
|
||||
#define ncx_slab_junk(p, size) ncx_memset(p, 0xA5, size)
|
||||
|
||||
#else
|
||||
|
||||
#define ncx_slab_junk(p, size)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ncx_slab_page_t* ncx_slab_alloc_pages(ncx_slab_pool_t* pool,
|
||||
ncx_uint_t pages);
|
||||
|
||||
static void ncx_slab_free_pages(ncx_slab_pool_t* pool, ncx_slab_page_t* page,
|
||||
ncx_uint_t pages);
|
||||
|
||||
static bool ncx_slab_empty(ncx_slab_pool_t* pool, ncx_slab_page_t* page);
|
||||
|
||||
static ncx_uint_t ncx_slab_max_size;
|
||||
static ncx_uint_t ncx_slab_exact_size;
|
||||
static ncx_uint_t ncx_slab_exact_shift;
|
||||
static ncx_uint_t ncx_pagesize;
|
||||
static ncx_uint_t ncx_pagesize_shift;
|
||||
static ncx_uint_t ncx_real_pages;
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_host.h>
|
||||
#endif
|
||||
|
||||
void ncx_slab_init(ncx_slab_pool_t* pool) {
|
||||
u_char* p;
|
||||
size_t size;
|
||||
ncx_uint_t i, n, pages;
|
||||
ncx_slab_page_t* slots;
|
||||
|
||||
/*pagesize*/
|
||||
#ifdef __APPLE__
|
||||
vm_size_t pagesize;
|
||||
host_page_size(mach_host_self(), &pagesize);
|
||||
ncx_pagesize = pagesize;
|
||||
#else
|
||||
ncx_pagesize = getpagesize();
|
||||
#endif
|
||||
for (n = ncx_pagesize, ncx_pagesize_shift = 0;
|
||||
n >>= 1; ncx_pagesize_shift++) {
|
||||
/* void */
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
if (ncx_slab_max_size == 0) {
|
||||
ncx_slab_max_size = ncx_pagesize / 2;
|
||||
ncx_slab_exact_size = ncx_pagesize / (8 * sizeof(uintptr_t));
|
||||
for (n = ncx_slab_exact_size; n >>= 1; ncx_slab_exact_shift++) {
|
||||
/* void */
|
||||
}
|
||||
}
|
||||
|
||||
pool->min_size = 1 << pool->min_shift;
|
||||
|
||||
p = (u_char*) pool + sizeof(ncx_slab_pool_t);
|
||||
slots = (ncx_slab_page_t*) p;
|
||||
|
||||
n = ncx_pagesize_shift - pool->min_shift;
|
||||
for (i = 0; i < n; i++) {
|
||||
slots[i].slab = 0;
|
||||
slots[i].next = &slots[i];
|
||||
slots[i].prev = 0;
|
||||
}
|
||||
|
||||
p += n * sizeof(ncx_slab_page_t);
|
||||
|
||||
size = pool->end - p;
|
||||
ncx_slab_junk(p, size);
|
||||
|
||||
pages = (ncx_uint_t) (size / (ncx_pagesize + sizeof(ncx_slab_page_t)));
|
||||
|
||||
ncx_memzero(p, pages * sizeof(ncx_slab_page_t));
|
||||
|
||||
pool->pages = (ncx_slab_page_t*) p;
|
||||
|
||||
pool->free.prev = 0;
|
||||
pool->free.next = (ncx_slab_page_t*) p;
|
||||
|
||||
pool->pages->slab = pages;
|
||||
pool->pages->next = &pool->free;
|
||||
pool->pages->prev = (uintptr_t) &pool->free;
|
||||
|
||||
pool->start = (u_char*)
|
||||
ncx_align_ptr((uintptr_t) p + pages * sizeof(ncx_slab_page_t),
|
||||
ncx_pagesize);
|
||||
|
||||
ncx_real_pages = (pool->end - pool->start) / ncx_pagesize;
|
||||
pool->pages->slab = ncx_real_pages;
|
||||
}
|
||||
|
||||
|
||||
void* ncx_slab_alloc(ncx_slab_pool_t* pool, size_t size) {
|
||||
ncx_shmtx_lock(&pool->mutex);
|
||||
|
||||
void* p = ncx_slab_alloc_locked(pool, size);
|
||||
|
||||
ncx_shmtx_unlock(&pool->mutex);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void* ncx_slab_alloc_locked(ncx_slab_pool_t* pool, size_t size) {
|
||||
size_t s;
|
||||
uintptr_t p, n, m, mask,* bitmap;
|
||||
ncx_uint_t i, slot, shift, map;
|
||||
ncx_slab_page_t* page,* prev,* slots;
|
||||
|
||||
if (size >= ncx_slab_max_size) {
|
||||
// debug("slab alloc: %zu", size);
|
||||
|
||||
page = ncx_slab_alloc_pages(pool, (size >> ncx_pagesize_shift)
|
||||
+ ((size % ncx_pagesize) ? 1 : 0));
|
||||
if (page) {
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
p += (uintptr_t) pool->start;
|
||||
} else {
|
||||
p = 0;
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (size > pool->min_size) {
|
||||
shift = 1;
|
||||
for (s = size - 1; s >>= 1; shift++) {
|
||||
/* void */
|
||||
}
|
||||
slot = shift - pool->min_shift;
|
||||
} else {
|
||||
size = pool->min_size;
|
||||
shift = pool->min_shift;
|
||||
slot = 0;
|
||||
}
|
||||
|
||||
slots = (ncx_slab_page_t*) ((u_char*) pool + sizeof(ncx_slab_pool_t));
|
||||
page = slots[slot].next;
|
||||
|
||||
if (page->next != page) {
|
||||
if (shift < ncx_slab_exact_shift) {
|
||||
do {
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
bitmap = (uintptr_t*) (pool->start + p);
|
||||
|
||||
map = (1 << (ncx_pagesize_shift - shift))
|
||||
/ (sizeof(uintptr_t) * 8);
|
||||
|
||||
for (n = 0; n < map; n++) {
|
||||
if (bitmap[n] != NCX_SLAB_BUSY) {
|
||||
for (m = 1, i = 0; m; m <<= 1, i++) {
|
||||
if ((bitmap[n] & m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bitmap[n] |= m;
|
||||
|
||||
i = ((n * sizeof(uintptr_t) * 8) << shift)
|
||||
+ (i << shift);
|
||||
|
||||
if (bitmap[n] == NCX_SLAB_BUSY) {
|
||||
for (n = n + 1; n < map; n++) {
|
||||
if (bitmap[n] != NCX_SLAB_BUSY) {
|
||||
p = (uintptr_t) bitmap + i;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
prev = (ncx_slab_page_t*)
|
||||
(page->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
prev->next = page->next;
|
||||
page->next->prev = page->prev;
|
||||
|
||||
page->next = NULL;
|
||||
page->prev = NCX_SLAB_SMALL;
|
||||
}
|
||||
|
||||
p = (uintptr_t) bitmap + i;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
page = page->next;
|
||||
} while (page);
|
||||
} else if (shift == ncx_slab_exact_shift) {
|
||||
do {
|
||||
if (page->slab != NCX_SLAB_BUSY) {
|
||||
for (m = 1, i = 0; m; m <<= 1, i++) {
|
||||
if ((page->slab & m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
page->slab |= m;
|
||||
|
||||
if (page->slab == NCX_SLAB_BUSY) {
|
||||
prev = (ncx_slab_page_t*)
|
||||
(page->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
prev->next = page->next;
|
||||
page->next->prev = page->prev;
|
||||
|
||||
page->next = NULL;
|
||||
page->prev = NCX_SLAB_EXACT;
|
||||
}
|
||||
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
p += i << shift;
|
||||
p += (uintptr_t) pool->start;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
page = page->next;
|
||||
} while (page);
|
||||
} else {
|
||||
/* shift > ncx_slab_exact_shift */
|
||||
|
||||
n = ncx_pagesize_shift - (page->slab & NCX_SLAB_SHIFT_MASK);
|
||||
n = 1 << n;
|
||||
n = ((uintptr_t) 1 << n) - 1;
|
||||
mask = n << NCX_SLAB_MAP_SHIFT;
|
||||
|
||||
do {
|
||||
if ((page->slab & NCX_SLAB_MAP_MASK) != mask) {
|
||||
for (m = (uintptr_t) 1 << NCX_SLAB_MAP_SHIFT, i = 0;
|
||||
m & mask;
|
||||
m <<= 1, i++) {
|
||||
if ((page->slab & m)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
page->slab |= m;
|
||||
|
||||
if ((page->slab & NCX_SLAB_MAP_MASK) == mask) {
|
||||
prev = (ncx_slab_page_t*)
|
||||
(page->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
prev->next = page->next;
|
||||
page->next->prev = page->prev;
|
||||
|
||||
page->next = NULL;
|
||||
page->prev = NCX_SLAB_BIG;
|
||||
}
|
||||
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
p += i << shift;
|
||||
p += (uintptr_t) pool->start;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
page = page->next;
|
||||
} while (page);
|
||||
}
|
||||
}
|
||||
|
||||
page = ncx_slab_alloc_pages(pool, 1);
|
||||
|
||||
if (page) {
|
||||
if (shift < ncx_slab_exact_shift) {
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
bitmap = (uintptr_t*) (pool->start + p);
|
||||
|
||||
s = 1 << shift;
|
||||
n = (1 << (ncx_pagesize_shift - shift)) / 8 / s;
|
||||
|
||||
if (n == 0) {
|
||||
n = 1;
|
||||
}
|
||||
|
||||
bitmap[0] = (2 << n) - 1;
|
||||
|
||||
map = (1 << (ncx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);
|
||||
|
||||
for (i = 1; i < map; i++) {
|
||||
bitmap[i] = 0;
|
||||
}
|
||||
|
||||
page->slab = shift;
|
||||
page->next = &slots[slot];
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_SMALL;
|
||||
|
||||
slots[slot].next = page;
|
||||
|
||||
p = ((page - pool->pages) << ncx_pagesize_shift) + s * n;
|
||||
p += (uintptr_t) pool->start;
|
||||
|
||||
goto done;
|
||||
} else if (shift == ncx_slab_exact_shift) {
|
||||
page->slab = 1;
|
||||
page->next = &slots[slot];
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_EXACT;
|
||||
|
||||
slots[slot].next = page;
|
||||
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
p += (uintptr_t) pool->start;
|
||||
|
||||
goto done;
|
||||
} else {
|
||||
/* shift > ncx_slab_exact_shift */
|
||||
|
||||
page->slab = ((uintptr_t) 1 << NCX_SLAB_MAP_SHIFT) | shift;
|
||||
page->next = &slots[slot];
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_BIG;
|
||||
|
||||
slots[slot].next = page;
|
||||
|
||||
p = (page - pool->pages) << ncx_pagesize_shift;
|
||||
p += (uintptr_t) pool->start;
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
p = 0;
|
||||
|
||||
done:
|
||||
|
||||
// debug("slab alloc: %p", (void *)p);
|
||||
|
||||
return (void*) p;
|
||||
}
|
||||
|
||||
|
||||
void ncx_slab_free(ncx_slab_pool_t* pool, void* p) {
|
||||
ncx_shmtx_lock(&pool->mutex);
|
||||
|
||||
ncx_slab_free_locked(pool, p);
|
||||
|
||||
ncx_shmtx_unlock(&pool->mutex);
|
||||
}
|
||||
|
||||
|
||||
void ncx_slab_free_locked(ncx_slab_pool_t* pool, void* p) {
|
||||
size_t size;
|
||||
uintptr_t slab, m,* bitmap;
|
||||
ncx_uint_t n, type, slot, shift, map;
|
||||
ncx_slab_page_t* slots,* page;
|
||||
|
||||
// debug("slab free: %p", p);
|
||||
|
||||
if ((u_char*) p < pool->start || (u_char*) p > pool->end) {
|
||||
// error("ncx_slab_free(): outside of pool");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
n = ((u_char*) p - pool->start) >> ncx_pagesize_shift;
|
||||
page = &pool->pages[n];
|
||||
slab = page->slab;
|
||||
type = page->prev & NCX_SLAB_PAGE_MASK;
|
||||
|
||||
switch (type) {
|
||||
case NCX_SLAB_SMALL:
|
||||
|
||||
shift = slab & NCX_SLAB_SHIFT_MASK;
|
||||
size = 1 << shift;
|
||||
|
||||
if ((uintptr_t) p & (size - 1)) {
|
||||
goto wrong_chunk;
|
||||
}
|
||||
|
||||
n = ((uintptr_t) p & (ncx_pagesize - 1)) >> shift;
|
||||
m = (uintptr_t) 1 << (n & (sizeof(uintptr_t) * 8 - 1));
|
||||
n /= (sizeof(uintptr_t) * 8);
|
||||
bitmap = (uintptr_t*) ((uintptr_t) p & ~(ncx_pagesize - 1));
|
||||
|
||||
if (bitmap[n] & m) {
|
||||
if (page->next == NULL) {
|
||||
slots = (ncx_slab_page_t*)
|
||||
((u_char*) pool + sizeof(ncx_slab_pool_t));
|
||||
slot = shift - pool->min_shift;
|
||||
|
||||
page->next = slots[slot].next;
|
||||
slots[slot].next = page;
|
||||
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_SMALL;
|
||||
page->next->prev = (uintptr_t) page | NCX_SLAB_SMALL;
|
||||
}
|
||||
|
||||
bitmap[n] &= ~m;
|
||||
|
||||
n = (1 << (ncx_pagesize_shift - shift)) / 8 / (1 << shift);
|
||||
|
||||
if (n == 0) {
|
||||
n = 1;
|
||||
}
|
||||
|
||||
if (bitmap[0] & ~(((uintptr_t) 1 << n) - 1)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
map = (1 << (ncx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);
|
||||
|
||||
for (n = 1; n < map; n++) {
|
||||
if (bitmap[n]) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ncx_slab_free_pages(pool, page, 1);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
goto chunk_already_free;
|
||||
|
||||
case NCX_SLAB_EXACT:
|
||||
|
||||
m = (uintptr_t) 1 <<
|
||||
(((uintptr_t) p & (ncx_pagesize - 1)) >> ncx_slab_exact_shift);
|
||||
size = ncx_slab_exact_size;
|
||||
|
||||
if ((uintptr_t) p & (size - 1)) {
|
||||
goto wrong_chunk;
|
||||
}
|
||||
|
||||
if (slab & m) {
|
||||
if (slab == NCX_SLAB_BUSY) {
|
||||
slots = (ncx_slab_page_t*)
|
||||
((u_char*) pool + sizeof(ncx_slab_pool_t));
|
||||
slot = ncx_slab_exact_shift - pool->min_shift;
|
||||
|
||||
page->next = slots[slot].next;
|
||||
slots[slot].next = page;
|
||||
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_EXACT;
|
||||
page->next->prev = (uintptr_t) page | NCX_SLAB_EXACT;
|
||||
}
|
||||
|
||||
page->slab &= ~m;
|
||||
|
||||
if (page->slab) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ncx_slab_free_pages(pool, page, 1);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
goto chunk_already_free;
|
||||
|
||||
case NCX_SLAB_BIG:
|
||||
|
||||
shift = slab & NCX_SLAB_SHIFT_MASK;
|
||||
size = 1 << shift;
|
||||
|
||||
if ((uintptr_t) p & (size - 1)) {
|
||||
goto wrong_chunk;
|
||||
}
|
||||
|
||||
m = (uintptr_t) 1 << ((((uintptr_t) p & (ncx_pagesize - 1)) >> shift)
|
||||
+ NCX_SLAB_MAP_SHIFT);
|
||||
|
||||
if (slab & m) {
|
||||
if (page->next == NULL) {
|
||||
slots = (ncx_slab_page_t*)
|
||||
((u_char*) pool + sizeof(ncx_slab_pool_t));
|
||||
slot = shift - pool->min_shift;
|
||||
|
||||
page->next = slots[slot].next;
|
||||
slots[slot].next = page;
|
||||
|
||||
page->prev = (uintptr_t) &slots[slot] | NCX_SLAB_BIG;
|
||||
page->next->prev = (uintptr_t) page | NCX_SLAB_BIG;
|
||||
}
|
||||
|
||||
page->slab &= ~m;
|
||||
|
||||
if (page->slab & NCX_SLAB_MAP_MASK) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ncx_slab_free_pages(pool, page, 1);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
goto chunk_already_free;
|
||||
|
||||
case NCX_SLAB_PAGE:
|
||||
|
||||
if ((uintptr_t) p & (ncx_pagesize - 1)) {
|
||||
goto wrong_chunk;
|
||||
}
|
||||
|
||||
if (slab == NCX_SLAB_PAGE_FREE) {
|
||||
// alert("ncx_slab_free(): page is already free");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (slab == NCX_SLAB_PAGE_BUSY) {
|
||||
// alert("ncx_slab_free(): pointer to wrong page");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
n = ((u_char*) p - pool->start) >> ncx_pagesize_shift;
|
||||
size = slab & ~NCX_SLAB_PAGE_START;
|
||||
|
||||
ncx_slab_free_pages(pool, &pool->pages[n], size);
|
||||
|
||||
ncx_slab_junk(p, size << ncx_pagesize_shift);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* not reached */
|
||||
|
||||
return;
|
||||
|
||||
done:
|
||||
|
||||
ncx_slab_junk(p, size);
|
||||
|
||||
return;
|
||||
|
||||
wrong_chunk:
|
||||
|
||||
// error("ncx_slab_free(): pointer to wrong chunk");
|
||||
|
||||
goto fail;
|
||||
|
||||
chunk_already_free:
|
||||
|
||||
// error("ncx_slab_free(): chunk is already free");
|
||||
|
||||
fail:
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static ncx_slab_page_t* ncx_slab_alloc_pages(ncx_slab_pool_t* pool, ncx_uint_t pages) {
|
||||
ncx_slab_page_t* page,* p;
|
||||
|
||||
for (page = pool->free.next; page != &pool->free; page = page->next) {
|
||||
if (page->slab >= pages) {
|
||||
if (page->slab > pages) {
|
||||
page[pages].slab = page->slab - pages;
|
||||
page[pages].next = page->next;
|
||||
page[pages].prev = page->prev;
|
||||
|
||||
p = (ncx_slab_page_t*) page->prev;
|
||||
p->next = &page[pages];
|
||||
page->next->prev = (uintptr_t) &page[pages];
|
||||
} else {
|
||||
p = (ncx_slab_page_t*) page->prev;
|
||||
p->next = page->next;
|
||||
page->next->prev = page->prev;
|
||||
}
|
||||
|
||||
page->slab = pages | NCX_SLAB_PAGE_START;
|
||||
page->next = NULL;
|
||||
page->prev = NCX_SLAB_PAGE;
|
||||
|
||||
if (--pages == 0) {
|
||||
return page;
|
||||
}
|
||||
|
||||
for (p = page + 1; pages; pages--) {
|
||||
p->slab = NCX_SLAB_PAGE_BUSY;
|
||||
p->next = NULL;
|
||||
p->prev = NCX_SLAB_PAGE;
|
||||
p++;
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
}
|
||||
|
||||
// error("ncx_slab_alloc() failed: no memory");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ncx_slab_free_pages(ncx_slab_pool_t* pool, ncx_slab_page_t* page,
|
||||
ncx_uint_t pages) {
|
||||
ncx_slab_page_t* prev,* next;
|
||||
|
||||
if (pages > 1) {
|
||||
ncx_memzero(&page[1], (pages - 1)* sizeof(ncx_slab_page_t));
|
||||
}
|
||||
|
||||
if (page->next) {
|
||||
prev = (ncx_slab_page_t*) (page->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
prev->next = page->next;
|
||||
page->next->prev = page->prev;
|
||||
}
|
||||
|
||||
page->slab = pages;
|
||||
page->prev = (uintptr_t) &pool->free;
|
||||
page->next = pool->free.next;
|
||||
page->next->prev = (uintptr_t) page;
|
||||
|
||||
pool->free.next = page;
|
||||
|
||||
#ifdef PAGE_MERGE
|
||||
if (pool->pages != page) {
|
||||
prev = page - 1;
|
||||
if (ncx_slab_empty(pool, prev)) {
|
||||
for (; prev >= pool->pages; prev--) {
|
||||
if (prev->slab != 0)
|
||||
{
|
||||
pool->free.next = page->next;
|
||||
page->next->prev = (uintptr_t) &pool->free;
|
||||
|
||||
prev->slab += pages;
|
||||
ncx_memzero(page, sizeof(ncx_slab_page_t));
|
||||
|
||||
page = prev;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((page - pool->pages + page->slab) < ncx_real_pages) {
|
||||
next = page + page->slab;
|
||||
if (ncx_slab_empty(pool, next))
|
||||
{
|
||||
prev = (ncx_slab_page_t *) (next->prev);
|
||||
prev->next = next->next;
|
||||
next->next->prev = next->prev;
|
||||
|
||||
page->slab += next->slab;
|
||||
ncx_memzero(next, sizeof(ncx_slab_page_t));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void ncx_slab_dummy_init(ncx_slab_pool_t* pool) {
|
||||
ncx_uint_t n;
|
||||
|
||||
#ifdef __APPLE__
|
||||
vm_size_t pagesize;
|
||||
host_page_size(mach_host_self(), &pagesize);
|
||||
ncx_pagesize = pagesize;
|
||||
#else
|
||||
ncx_pagesize = getpagesize();
|
||||
#endif
|
||||
for (n = ncx_pagesize, ncx_pagesize_shift = 0;
|
||||
n >>= 1; ncx_pagesize_shift++) {
|
||||
/* void */
|
||||
}
|
||||
|
||||
if (ncx_slab_max_size == 0) {
|
||||
ncx_slab_max_size = ncx_pagesize / 2;
|
||||
ncx_slab_exact_size = ncx_pagesize / (8 * sizeof(uintptr_t));
|
||||
for (n = ncx_slab_exact_size; n >>= 1; ncx_slab_exact_shift++) {
|
||||
/* void */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ncx_slab_stat(ncx_slab_pool_t* pool, ncx_slab_stat_t* stat) {
|
||||
uintptr_t m, n, mask, slab;
|
||||
uintptr_t* bitmap;
|
||||
ncx_uint_t i, j, map, type, obj_size;
|
||||
ncx_slab_page_t* page;
|
||||
|
||||
ncx_memzero(stat, sizeof(ncx_slab_stat_t));
|
||||
|
||||
page = pool->pages;
|
||||
stat->pages = (pool->end - pool->start) / ncx_pagesize;;
|
||||
|
||||
for (i = 0; i < stat->pages; i++) {
|
||||
slab = page->slab;
|
||||
type = page->prev & NCX_SLAB_PAGE_MASK;
|
||||
|
||||
switch (type) {
|
||||
case NCX_SLAB_SMALL:
|
||||
|
||||
n = (page - pool->pages) << ncx_pagesize_shift;
|
||||
bitmap = (uintptr_t*) (pool->start + n);
|
||||
|
||||
obj_size = 1 << slab;
|
||||
map = (1 << (ncx_pagesize_shift - slab))
|
||||
/ (sizeof(uintptr_t) * 8);
|
||||
|
||||
for (j = 0; j < map; j++) {
|
||||
for (m = 1; m; m <<= 1) {
|
||||
if ((bitmap[j] & m)) {
|
||||
stat->used_size += obj_size;
|
||||
stat->b_small += obj_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stat->p_small++;
|
||||
|
||||
break;
|
||||
|
||||
case NCX_SLAB_EXACT:
|
||||
|
||||
if (slab == NCX_SLAB_BUSY) {
|
||||
stat->used_size += sizeof(uintptr_t) * 8 * ncx_slab_exact_size;
|
||||
stat->b_exact += sizeof(uintptr_t) * 8 * ncx_slab_exact_size;
|
||||
} else {
|
||||
for (m = 1; m; m <<= 1) {
|
||||
if (slab & m) {
|
||||
stat->used_size += ncx_slab_exact_size;
|
||||
stat->b_exact += ncx_slab_exact_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stat->p_exact++;
|
||||
|
||||
break;
|
||||
|
||||
case NCX_SLAB_BIG:
|
||||
|
||||
j = ncx_pagesize_shift - (slab & NCX_SLAB_SHIFT_MASK);
|
||||
j = 1 << j;
|
||||
j = ((uintptr_t) 1 << j) - 1;
|
||||
mask = j << NCX_SLAB_MAP_SHIFT;
|
||||
obj_size = 1 << (slab & NCX_SLAB_SHIFT_MASK);
|
||||
|
||||
for (m = (uintptr_t) 1 << NCX_SLAB_MAP_SHIFT; m & mask; m <<= 1) {
|
||||
if ((page->slab & m)) {
|
||||
stat->used_size += obj_size;
|
||||
stat->b_big += obj_size;
|
||||
}
|
||||
}
|
||||
|
||||
stat->p_big++;
|
||||
|
||||
break;
|
||||
|
||||
case NCX_SLAB_PAGE:
|
||||
|
||||
if (page->prev == NCX_SLAB_PAGE) {
|
||||
slab = slab & ~NCX_SLAB_PAGE_START;
|
||||
stat->used_size += slab * ncx_pagesize;
|
||||
stat->b_page += slab * ncx_pagesize;
|
||||
stat->p_page += slab;
|
||||
|
||||
i += (slab - 1);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
if (slab > stat->max_free_pages) {
|
||||
stat->max_free_pages = page->slab;
|
||||
}
|
||||
|
||||
stat->free_page += slab;
|
||||
|
||||
i += (slab - 1);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
page = pool->pages + i + 1;
|
||||
}
|
||||
|
||||
stat->pool_size = pool->end - pool->start;
|
||||
stat->used_pct = stat->used_size * 100 / stat->pool_size;
|
||||
|
||||
// info("pool_size : %zu bytes", stat->pool_size);
|
||||
// info("used_size : %zu bytes", stat->used_size);
|
||||
// info("used_pct : %zu%%\n", stat->used_pct);
|
||||
|
||||
// info("total page count : %zu", stat->pages);
|
||||
// info("free page count : %zu\n", stat->free_page);
|
||||
|
||||
// info("small slab use page : %zu,\tbytes : %zu", stat->p_small, stat->b_small);
|
||||
// info("exact slab use page : %zu,\tbytes : %zu", stat->p_exact, stat->b_exact);
|
||||
// info("big slab use page : %zu,\tbytes : %zu", stat->p_big, stat->b_big);
|
||||
// info("page slab use page : %zu,\tbytes : %zu\n", stat->p_page, stat->b_page);
|
||||
|
||||
// info("max free pages : %zu\n", stat->max_free_pages);
|
||||
}
|
||||
|
||||
static bool ncx_slab_empty(ncx_slab_pool_t* pool, ncx_slab_page_t* page) {
|
||||
ncx_slab_page_t* prev;
|
||||
|
||||
if (page->slab == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//page->prev == PAGE | SMALL | EXACT | BIG
|
||||
if (page->next == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prev = (ncx_slab_page_t*) (page->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
while (prev >= pool->pages) {
|
||||
prev = (ncx_slab_page_t*) (prev->prev & ~NCX_SLAB_PAGE_MASK);
|
||||
};
|
||||
|
||||
if (prev == &pool->free) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
1133
third_party/mempool/mempool/ncx_slab.c.orz
vendored
1133
third_party/mempool/mempool/ncx_slab.c.orz
vendored
File diff suppressed because it is too large
Load Diff
56
third_party/mempool/mempool/ncx_slab.h
vendored
56
third_party/mempool/mempool/ncx_slab.h
vendored
@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ncx_core.h"
|
||||
#include "ncx_lock.h"
|
||||
|
||||
#include "extern.h"
|
||||
|
||||
typedef struct ncx_slab_page_s ncx_slab_page_t;
|
||||
|
||||
struct ncx_slab_page_s {
|
||||
uintptr_t slab;
|
||||
ncx_slab_page_t *next;
|
||||
uintptr_t prev;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t min_size;
|
||||
size_t min_shift;
|
||||
|
||||
ncx_slab_page_t *pages;
|
||||
ncx_slab_page_t free;
|
||||
|
||||
u_char *start;
|
||||
u_char *end;
|
||||
|
||||
ncx_shmtx_t mutex;
|
||||
|
||||
void *addr;
|
||||
} ncx_slab_pool_t;
|
||||
|
||||
typedef struct {
|
||||
size_t pool_size, used_size, used_pct;
|
||||
size_t pages, free_page;
|
||||
size_t p_small, p_exact, p_big, p_page; /* 四种slab占用的page数 */
|
||||
size_t b_small, b_exact, b_big, b_page; /* 四种slab占用的byte数 */
|
||||
size_t max_free_pages; /* 最大的连续可用page数 */
|
||||
} ncx_slab_stat_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { // only need to export C interface if
|
||||
// used by C++ source code
|
||||
#endif
|
||||
|
||||
MEMPOOL_API void ncx_slab_init(ncx_slab_pool_t *pool);
|
||||
MEMPOOL_API void* ncx_slab_alloc(ncx_slab_pool_t *pool, size_t size);
|
||||
MEMPOOL_API void* ncx_slab_alloc_locked(ncx_slab_pool_t *pool, size_t size);
|
||||
MEMPOOL_API void ncx_slab_free(ncx_slab_pool_t *pool, void *p);
|
||||
MEMPOOL_API void ncx_slab_free_locked(ncx_slab_pool_t *pool, void *p);
|
||||
|
||||
MEMPOOL_API void ncx_slab_dummy_init(ncx_slab_pool_t *pool);
|
||||
MEMPOOL_API void ncx_slab_stat(ncx_slab_pool_t *pool, ncx_slab_stat_t *stat);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
2
third_party/taskflow
vendored
2
third_party/taskflow
vendored
@ -1 +1 @@
|
||||
Subproject commit fc27de3eae7d35ec3a7dea6ab37da786d7ccacdb
|
||||
Subproject commit 598fa70633c10b2ae7354b976b4976bf40502405
|
Loading…
x
Reference in New Issue
Block a user