midi_sequencer

This commit is contained in:
Nanako 2024-06-08 13:41:29 +08:00
parent 1b4ee22c95
commit 580ad03623
16 changed files with 163 additions and 33 deletions

View File

@ -1,6 +1,6 @@
#pragma once
#include <map>
#include <string>
#include <unordered_map>
class command_line {
public:
@ -20,6 +20,6 @@ public:
void get_arg(const std::string& key, bool& out) const;
private:
std::map<std::string, std::string> args_; // key-value pairs
std::unordered_map<std::string, std::string> args_; // key-value pairs
command_line() = default;
};

View File

@ -38,7 +38,8 @@ void audio_device_manager::release(singleton_release_guard& release_guard) {
singleton_t<audio_device_manager>::release(release_guard);
stop_render_thread();
release_guard.require_release<mixer>();
audio_->stopStream();
if (audio_)
audio_->stopStream();
delete audio_;
}
@ -76,6 +77,7 @@ uint32_t audio_device_manager::get_sample_rate() const {
void audio_device_manager::set_sample_rate(uint32_t sample_rate) {
sample_rate_ = sample_rate;
on_sample_rate_changed.broadcast(sample_rate_);
}
uint32_t audio_device_manager::get_buffer_size() const {
@ -164,7 +166,7 @@ void audio_device_manager::render_thread() {
// std::this_thread::yield();
continue;
}
g_midi_sequencer.process(rate, frames);
g_midi_sequencer.process(get_sample_rate(), frames);
g_mixer.reset();
g_plugin_host_manager.process(frames);
g_mixer.process(frames);

View File

@ -3,6 +3,7 @@
#include "audio/misc/circular_audio_buffer.h"
#include "RtAudio.h"
#include "misc/delegates.h"
class audio_device_manager : public singleton_t<audio_device_manager> {
public:
@ -23,6 +24,8 @@ public:
int stream_callback(sample_t* output, sample_t* input, unsigned long frame_count, double stream_time, RtAudioStreamStatus status);
void log_all_devices();
void log_current_device_info();
multicast_delegate<uint32_t> on_sample_rate_changed;
protected:
#pragma region render_thread
void start_render_thread();

View File

@ -92,22 +92,22 @@ void channel_interface::set_output_channel_node_name(uint32_t node_index, uint32
output_channel_names_[node_index][channel_index] = name;
}
std::map<uint32_t, std::string> channel_interface::get_input_channel_node_name(uint32_t node_index) {
std::unordered_map<uint32_t, std::string> channel_interface::get_input_channel_node_name(uint32_t node_index) {
return input_channel_names_[node_index];
}
std::map<uint32_t, std::string> channel_interface::get_input_channel_node_name(channel_node* node) {
std::unordered_map<uint32_t, std::string> channel_interface::get_input_channel_node_name(channel_node* node) {
uint32_t index = get_input_node_index(node);
if (index == -1)
return {};
return get_input_channel_node_name(index);
}
std::map<uint32_t, std::string> channel_interface::get_output_channel_node_name(uint32_t node_index) {
std::unordered_map<uint32_t, std::string> channel_interface::get_output_channel_node_name(uint32_t node_index) {
return output_channel_names_[node_index];
}
std::map<uint32_t, std::string> channel_interface::get_output_channel_node_name(channel_node* node) {
std::unordered_map<uint32_t, std::string> channel_interface::get_output_channel_node_name(channel_node* node) {
uint32_t index = get_output_node_index(node);
if (index == -1)
return {};

View File

@ -1,6 +1,6 @@
#pragma once
#include <cstdint>
#include <map>
#include <unordered_map>
#include <string>
#include <vector>
@ -27,18 +27,18 @@ public:
void set_input_channel_node_name(uint32_t node_index, uint32_t channel_index, const std::string& name);
void set_output_channel_node_name(uint32_t node_index, uint32_t channel_index, const std::string& name);
std::map<uint32_t, std::string> get_input_channel_node_name(uint32_t node_index);
std::map<uint32_t, std::string> get_input_channel_node_name(channel_node* node);
std::map<uint32_t, std::string> get_output_channel_node_name(uint32_t node_index);
std::map<uint32_t, std::string> get_output_channel_node_name(channel_node* node);
std::unordered_map<uint32_t, std::string> get_input_channel_node_name(uint32_t node_index);
std::unordered_map<uint32_t, std::string> get_input_channel_node_name(channel_node* node);
std::unordered_map<uint32_t, std::string> get_output_channel_node_name(uint32_t node_index);
std::unordered_map<uint32_t, std::string> get_output_channel_node_name(channel_node* node);
void remove_track(mixer_track* track);
std::vector<channel_node*> input_channel_nodes;
std::vector<channel_node*> output_channel_nodes;
private:
std::map<uint32_t, std::map<uint32_t, std::string>> input_channel_names_;
std::map<uint32_t, std::map<uint32_t, std::string>> output_channel_names_;
std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> input_channel_names_;
std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> output_channel_names_;
std::vector<sample_t*> input_headers_;
std::vector<sample_t*> output_headers_;
};

View File

@ -11,7 +11,7 @@
IMPL_SINGLETON_INSTANCE(mixer)
void build_effect_channel_interface(mixer_track* track, const channel_interface* in_interface, std::map<mixer_track*, int32_t>& 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;
@ -38,7 +38,7 @@ void build_effect_channel_interface(mixer_track* track, const channel_interface*
}
}
int32_t build_process_node_internal(mixer_track* track, std::map<mixer_track*, int32_t>& processed_tracks, int32_t layer) {
int32_t build_process_node_internal(mixer_track* track, std::unordered_map<mixer_track*, int32_t>& processed_tracks, int32_t layer) {
int32_t& track_current_layer = processed_tracks[track];
track_current_layer = std::max(track_current_layer, layer);
for (const mixer_track_link& child_link: track->children) {
@ -51,7 +51,7 @@ int32_t build_process_node_internal(mixer_track* track, std::map<mixer_track*, i
return ++layer;
}
void build_instrument_process_node(const plugin_host* host, std::map<mixer_track*, int32_t>& 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) {
build_effect_channel_interface(Track, host->channel, processed_tracks);
}
@ -140,7 +140,7 @@ void mixer::reset() {
void mixer::build_process_node() {
if (tracks_.empty())
return;
std::map<mixer_track*, int32_t> processed_tracks;
std::unordered_map<mixer_track*, int32_t> processed_tracks;
auto* master = get_master();
build_process_node_internal(master, processed_tracks, 0);
const auto& instruments = g_plugin_host_manager.get_plugin_hosts();
@ -235,6 +235,7 @@ void mixer::update_taskflow(uint32_t in_frames) {
void mixer::begin_release(singleton_release_guard &release_guard) {
singleton::begin_release(release_guard);
auto e = executor_.exchange(nullptr);
e->wait_for_all();
if (e)
e->wait_for_all();
delete e;
}

View File

@ -47,7 +47,7 @@ private:
void update_taskflow(uint32_t in_frames);
std::vector<mixer_track*> tracks_;
std::map<int32_t, std::vector<mixer_track*>> layer_tracks_;
std::unordered_map<int32_t, std::vector<mixer_track*>> layer_tracks_;
std::vector<int32_t> layer_order_;
std::atomic<tf::Executor*> executor_;
tf::Taskflow taskflow_;

View File

@ -1,15 +1,44 @@
#include "midi_sequencer.h"
#include "audio/device/audio_device_manager.h"
#include "misc/tick.h"
#include "vst2/vst2_plugin_host.h"
IMPL_SINGLETON_INSTANCE(midi_sequencer)
void midi_sequencer::init(singleton_initliazer& initliazer) {
singleton_t<midi_sequencer>::init(initliazer);
vst2_plugin_host::vst_time_info.tempo = 128.0;
auto adm = initliazer.require<audio_device_manager>();
adm->on_sample_rate_changed.add_raw(this, &midi_sequencer::on_sample_rate_changed);
set_bpm(140.0);
set_tpq(960);
}
void midi_sequencer::process(double sample_rate, uint32_t in_frames) {
void midi_sequencer::process(double sample_rate, uint32_t delta_frames) {
update_time(sample_rate, delta_frames);
update_vst2_time_info();
}
void midi_sequencer::set_bpm(double in_bpm) {
bpm_ = std::max(in_bpm, 1.0);
vst2_time.tempo = bpm_;
}
void midi_sequencer::set_tpq(int32_t in_tpq) {
tpq_ = in_tpq;
}
void midi_sequencer::on_sample_rate_changed(uint32_t sample_rate) {
vst2_time.sampleRate = sample_rate;
}
void midi_sequencer::update_time(double sample_rate, uint32_t delta_frames) {
audio_frame frame(delta_frames);
}
void midi_sequencer::update_vst2_time_info() {
vst2_time.nanoSeconds = 0;
vst2_time.ppqPos = tpq_pos_;
}

View File

@ -1,14 +1,35 @@
#pragma once
#include <cstdint>
#include "misc/tick.h"
#include "misc/singleton/singleton.h"
class midi_sequencer : public singleton_t<midi_sequencer> {
public:
void init(singleton_initliazer& initliazer) override;
void process(double sample_rate, uint32_t in_frames);
void process(double sample_rate, uint32_t delta_frames);
const char* get_name() override { return "midi_sequencer"; }
void set_bpm(double in_bpm);
void set_tpq(int32_t in_tpq);
[[nodiscard]] double get_bpm() const {
return bpm_;
}
[[nodiscard]] int32_t get_tpq() const {
return tpq_;
}
protected:
void on_sample_rate_changed(uint32_t sample_rate);
void update_time(double sample_rate, uint32_t delta_frames);
private:
void update_vst2_time_info();
double bpm_ = 0;
int32_t tpq_ = 0;
audio_frame sample_pos_ = 0;
int32_t tpq_pos_ = 0;
double nano_seconds_ = 0;
};
DEFINE_SINGLETON_INSTANCE(midi_sequencer)

View File

@ -116,6 +116,7 @@ void plugin_host_manager::update_taskflow(uint32_t in_frames) {
void plugin_host_manager::begin_release(singleton_release_guard& release_guard) {
auto executor = executor_.exchange(nullptr);
executor->wait_for_all();
if (executor)
executor->wait_for_all();
delete executor;
}

View File

@ -4,10 +4,10 @@
#include "audio/mixer/channel_interface.h"
#include "window/window_manager.h"
std::map<std::string, std::weak_ptr<dynamic_library>> vst2_library_map;
std::unordered_map<std::string, std::weak_ptr<dynamic_library>> vst2_library_map;
VstTimeInfo vst2_plugin_host::vst_time_info{};
std::map<std::string, bool> can_do_map =
std::unordered_map<std::string, bool> can_do_map =
{
{"sendVstEvents", true},
{"sendVstMidiEvent", true},
@ -73,7 +73,7 @@ VstIntPtr vst_master_callback(AEffect* effect, VstInt32 opcode, VstInt32 index,
return 1;
}
case audioMasterGetTime:
return (VstIntPtr)&vst2_plugin_host::vst_time_info;
return (VstIntPtr)&vst2_time;
case audioMasterIdle:
// UE_LOG(LogTemp, Log, TEXT("Plugin Idle"));
return 1;

View File

@ -43,3 +43,5 @@ private:
std::shared_ptr<dynamic_library> library_;
bool enabled_ = false;
};
#define vst2_time vst2_plugin_host::vst_time_info

View File

@ -46,7 +46,7 @@ public:
#define DEFINE_SINGLETON_INSTANCE(T) \
inline T g_##T; \
extern "C" CORE_API T* get_##T();
#else
#else
#define DEFINE_SINGLETON_INSTANCE(T) \
extern "C" CORE_API T* get_##T();
#endif

29
core/misc/tick.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "tick.h"
#include "audio/device/audio_device_manager.h"
#include "audio/plugin_host/midi_sequencer.h"
midi_tick::midi_tick(const audio_frame& in_frame) {
auto bpm = g_midi_sequencer.get_bpm();
auto tpq = g_midi_sequencer.get_tpq();
auto sample_rate = g_audio_device_manager.get_sample_rate();
double time_in_seconds = (double)in_frame.get_frames() / sample_rate;
ticks = time_in_seconds * bpm * tpq / 60.0;
}
audio_frame midi_tick::to_audio_frame() const {
return audio_frame(*this);
}
audio_frame::audio_frame(const midi_tick& in_tick) {
auto bpm = g_midi_sequencer.get_bpm();
auto tpq = g_midi_sequencer.get_tpq();
auto sample_rate = g_audio_device_manager.get_sample_rate();
double tick_duration = 60.0 / (bpm * tpq);
double tick_in_seconds = in_tick.get_ticks() * tick_duration;
frames = tick_in_seconds * sample_rate;
}
midi_tick audio_frame::to_midi_tick() const {
return midi_tick(*this);
}

42
core/misc/tick.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#include <cstdint>
class audio_frame;
class midi_tick {
public:
midi_tick(int64_t in_ticks) : ticks(in_ticks) {
}
explicit midi_tick(const audio_frame& in_frame);
[[nodiscard]] audio_frame to_audio_frame() const;
[[nodiscard]] int64_t get_ticks() const {
return ticks;
}
operator int64_t() const {
return ticks;
}
private:
int64_t ticks;
};
class audio_frame {
public:
audio_frame(int64_t in_frames) : frames(in_frames) {
}
explicit audio_frame(const midi_tick& in_tick);
[[nodiscard]] midi_tick to_midi_tick() const;
[[nodiscard]] int64_t get_frames() const {
return frames;
}
operator int64_t() const {
return frames;
}
auto operator<=>(const audio_frame&) const = default;
private:
int64_t frames;
};

View File

@ -1,7 +1,7 @@
#pragma once
#include "GLFW/glfw3.h"
#include "misc/singleton/singleton.h"
#include <map>
#include <unordered_map>
#include <memory>
#include <thread>
@ -27,7 +27,7 @@ private:
std::shared_ptr<GLFWwindow> window;
};
void on_host_window_close(GLFWwindow* window);
std::map<plugin_host*, host_info> host_infos_;
std::unordered_map<plugin_host*, host_info> host_infos_;
};
DEFINE_SINGLETON_INSTANCE(window_manager)