midi播放测试

This commit is contained in:
Nanako 2024-07-17 19:18:07 +08:00
parent e0539cb4e1
commit e77638960c
23 changed files with 412 additions and 92 deletions

View File

@ -4,7 +4,7 @@
class clip {
public:
virtual ~clip() = default;
virtual auto get_length() -> midi_tick = 0;
virtual void process(midi_tick tick, midi_tick length) = 0;
virtual auto get_length() -> uint64_t = 0;
virtual void process(uint64_t tick, uint64_t length) = 0;
virtual void reset() = 0;
};

View File

@ -1 +1,31 @@
#include "clip_instance.h"
#include "clip.h"
clip_instance::clip_instance(clip* clip): clip_(clip) {
clip_end_ = clip->get_length();
}
void clip_instance::process(int64_t sample_pos, int32_t length) {
const int64_t clip_end = clip_end_;
if (sample_pos >= clip_end) {
return;
}
const int64_t clip_begin = clip_begin_;
if (sample_pos + length <= clip_begin) {
return;
}
const int64_t clip_length = clip_end - clip_begin;
const int64_t clip_pos = sample_pos - clip_begin;
const int64_t clip_length_remaining = clip_length - clip_pos;
const int64_t length_remaining = length - clip_length_remaining;
if (clip_length_remaining < length) {
clip_->process(clip_pos, clip_length_remaining);
clip_begin_ = clip_end;
if (length_remaining > 0) {
process(sample_pos + clip_length_remaining, length_remaining);
}
} else {
clip_->process(clip_pos, length);
}
}

View File

@ -1,6 +1,15 @@
#pragma once
#include "misc/mempool.h"
class clip_instance : public pool_obj<clip_instance> {
class clip;
class clip_instance : public pool_obj<clip_instance> {
public:
clip_instance(clip* clip);
void process(int64_t sample_pos, int32_t length);
private:
clip* clip_;
int64_t clip_begin_ = 0;
int64_t clip_end_ = 0;
};

View File

@ -1,16 +1,30 @@
#include "midi_clip.h"
void midi_track::process(midi_tick tick, midi_tick length) {
#include "audio/plugin_host/plugin_host.h"
void midi_track::process(uint64_t tick, uint64_t length) {
const uint64_t end_tick = tick + length;
for (; last_index_ < events_.size(); ++last_index_) {
auto& event = events_[last_index_];
if (event.tick >= end_tick) {
break;
}
host_->
host_->push_midi(event, event.tick - tick);
}
}
void midi_clip::process(midi_tick tick, midi_tick length) {
auto end_tick = tick += length;
void midi_clip::update_length() {
length_ = 0;
for (const auto [instrument, track] : tracks_) {
const smf::MidiEventList& list = track->get_track();
if (!list.size())
continue;
length_ = std::max<uint64_t>(length_, list.back().tick);
}
}
void midi_clip::process(uint64_t tick, uint64_t length) {
for (const auto [instrument, track] : tracks_) {
track->process(tick, length);
}
}

View File

@ -1,4 +1,6 @@
#pragma once
#include <unordered_map>
#include "clip.h"
#include "misc/mempool.h"
#include "MidiEventList.h"
@ -9,10 +11,17 @@ class plugin_host;
class midi_track : public pool_obj<midi_track> {
public:
midi_track(plugin_host* host) : host_(host) {}
void process(midi_tick tick, midi_tick length);
void process(uint64_t tick, uint64_t length);
void reset() {
last_index_ = 0;
}
[[nodiscard]] auto get_host() const -> plugin_host* {
return host_;
}
[[nodiscard]] auto get_track() -> smf::MidiEventList& {
return events_;
}
private:
plugin_host* host_;
smf::MidiEventList events_;
@ -21,16 +30,25 @@ private:
class midi_clip : public clip, public pool_obj<midi_clip> {
public:
auto get_length() -> midi_tick override {
void update_length();
auto get_length() -> uint64_t override {
return length_;
}
void process(midi_tick tick, midi_tick length) override;
void process(uint64_t tick, uint64_t length) override;
void reset() override {
for (auto track : tracks_) {
for (const auto [instrument, track] : tracks_) {
track->reset();
}
}
auto get_instrument_track(plugin_host* instrument) -> midi_track* {
if (tracks_.contains(instrument)) {
return tracks_[instrument];
}
auto new_track = new midi_track(instrument);
tracks_[instrument] = new_track;
return new_track;
}
private:
midi_tick length_;
std::vector<midi_track*> tracks_;
uint64_t length_ = 0;
std::unordered_map<plugin_host*, midi_track*> tracks_;
};

View File

@ -1,34 +1,82 @@
#include "midi_sequencer.h"
#include "MidiFile.h"
#include "audio/device/audio_device_manager.h"
#include "audio/plugin_host/plugin_host_manager.h"
#include "misc/tick.h"
#include "audio/plugin_host/vst2/vst2_plugin_host.h"
#include "clip/clip_instance.h"
#include "clip/midi_clip.h"
#include "thread_message/thread_message_hubs.h"
IMPL_SINGLETON_INSTANCE(midi_sequencer)
void midi_sequencer::test() {
const char* path = R"(F:\VST\VST64\4Front Piano x64.dll)";
// host->try_open_editor();
smf::MidiFile midifile("E:/Projects/AronaStudio/Arona/resources/1.mid");
if (!midifile.status())
spdlog::critical("Error reading MIDI file");
midifile.doTimeAnalysis();
midifile.linkNotePairs();
midifile.joinTracks();
auto c = get_midi_sequencer()->create_clip<midi_clip>();
auto track_count = midifile.getTrackCount();
for (int i = 0; i < track_count; ++i) {
plugin_host* host = get_plugin_host_manager()->create_instrument_plugin_host(path);
auto& t = midifile[i];
for (int j = 0; j < t.size(); ++j) {
auto& e = t[j];
midi_tick tick(e.tick);
e.tick = tick.to_audio_frame(midifile.getTPQ());
}
const auto track = c->get_instrument_track(host);
track->get_track() = midifile[i];
}
c->update_length();
g_audio_thread_hub.push_message([=, this]() {
create_clip_instance(c);
});
}
void midi_sequencer::init(singleton_initliazer& initliazer) {
singleton_t<midi_sequencer>::init(initliazer);
singleton_t::init(initliazer);
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 delta_frames) {
if (!playing_) {
return;
}
for (auto clip_ins : clip_instances_) {
clip_ins->process(sample_pos_, delta_frames);
}
update_time(sample_rate, delta_frames);
update_vst2_time_info();
}
auto midi_sequencer::create_clip_instance(clip* clip) -> clip_instance* {
auto instance = new clip_instance(clip);
clip_instances_.push_back(instance);
return instance;
}
void midi_sequencer::delete_clip(clip* clip) {
auto it = std::find(clips_.begin(), clips_.end(), clip);
if (it != clips_.end()) {
clips_.erase(it);
}
}
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;
}
@ -37,8 +85,9 @@ void midi_sequencer::update_time(double sample_rate, uint32_t delta_frames) {
auto clock = std::chrono::system_clock::now();
sample_pos_ += delta_frames;
nano_seconds_ = std::chrono::duration_cast<std::chrono::nanoseconds>(clock.time_since_epoch()).count();
tpq_pos_ = sample_pos_ * tpq_ / sample_rate;
tpq_pos_ = sample_pos_ * TPQ / sample_rate;
vst2_time.sampleRate = sample_rate;
update_vst2_time_info();
}

View File

@ -4,23 +4,38 @@
#include "misc/tick.h"
#include "misc/singleton/singleton.h"
class midi_sequencer : public singleton_t<midi_sequencer> {
class clip;
class clip_instance;
class CORE_API midi_sequencer : public singleton_t<midi_sequencer> {
public:
static constexpr int32_t TPQ = 960;
void test();
void init(singleton_initliazer& initliazer) override;
void process(double sample_rate, uint32_t delta_frames);
auto toggle_play() -> bool {
playing_ = !playing_;
return playing_;
}
const char* get_name() override { return "midi_sequencer"; }
template<class T>
auto create_clip() -> T* {
auto clip = new T();
clips_.push_back(clip);
return clip;
}
auto create_clip_instance(clip* clip) -> clip_instance*;
void delete_clip(clip* clip);
auto get_name() -> const char* override { return "midi_sequencer"; }
void set_bpm(double in_bpm);
void set_tpq(int32_t in_tpq);
[[nodiscard]] double get_bpm() const {
[[nodiscard]] auto get_bpm() const -> double {
return bpm_;
}
[[nodiscard]] int32_t get_tpq() const {
return tpq_;
}
uint64_t get_sample_pos() const {
[[nodiscard]] auto get_sample_pos() const -> double {
return sample_pos_;
}
protected:
@ -29,11 +44,13 @@ protected:
private:
void update_vst2_time_info();
bool playing_ = false;
double bpm_ = 0;
int32_t tpq_ = 0; // ticks per quarter note
audio_frame sample_pos_ = 0;
double sample_pos_ = 0;
int32_t tpq_pos_ = 0;
uint64_t nano_seconds_ = 0;
std::vector<clip*> clips_;
std::vector<clip_instance*> clip_instances_;
};
DEFINE_SINGLETON_INSTANCE(midi_sequencer)

View File

@ -13,7 +13,6 @@ void audio_clock::set_midi_tick(uint64_t tick) {
void audio_clock::update_midi_tick() {
auto sample_rate = get_audio_device_manager()->get_sample_rate();
auto tpq = get_midi_sequencer()->get_tpq();
double second = (double)sample_tick_ / sample_rate;
midi_tick_ = (uint64_t)(second * tpq);
midi_tick_ = (uint64_t)(second * midi_sequencer::TPQ);
}

View File

@ -4,16 +4,9 @@
#include <atomic>
#include "extern.h"
#include "misc/core_assert.h"
#include "spdlog/spdlog.h"
#if defined(_DEBUG) || defined(DEBUG)
#define checkf(expr, msg, ...) if (!(expr)) { spdlog::error(msg, __VA_ARGS__); throw std::runtime_error(fmt::format(msg, __VA_ARGS__)); }
#define check(expr) if (!(expr)) { spdlog::error("Check failed: {}", #expr); throw std::runtime_error(fmt::format("Check failed: {}", #expr)); }
#else
#define checkf(...)
#define check(...)
#endif
/**
* Basic implementation of a circular buffer built for pushing and popping arbitrary amounts of data at once.
* Designed to be thread safe for SPSC; However, if Push() and Pop() are both trying to access an overlapping area of the buffer,
@ -234,7 +227,7 @@ public:
// Will assert if the buffer is empty. Please check Num() > 0 before calling.
SampleType pop() {
// Calling this when the buffer is empty is considered a fatal error.
check(Num() > 0);
check(num() > 0);
SampleType* src_buffer = internal_buffer_.data();
const uint32_t read_index = read_counter_;
@ -246,7 +239,7 @@ public:
SampleType* get_header() {
// Calling this when the buffer is empty is considered a fatal error.
check(Num() > 0);
check(num() > 0);
// 根据read_counter_获取当前的header
return &internal_buffer_[read_counter_];
@ -298,6 +291,3 @@ public:
using circular_buffer_type = circular_audio_buffer<sample_t>;
using circular_buffer_vector_type = std::vector<circular_buffer_type>;
#undef checkf
#undef check

View File

@ -15,7 +15,7 @@
IMPL_SINGLETON_INSTANCE(plugin_host_manager)
void plugin_host_manager::init(singleton_initliazer& initliazer) {
singleton_t<plugin_host_manager>::init(initliazer);
singleton_t::init(initliazer);
auto* mixer_ptr = initliazer.require<mixer>();
mixer_ptr->on_remove_track.add_raw(this, &plugin_host_manager::on_mixer_track_removed);
}

View File

@ -7,7 +7,9 @@
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{};
VstTimeInfo vst2_plugin_host::vst_time_info{
.flags = kVstNanosValid | kVstPpqPosValid | kVstTempoValid | kVstBarsValid | kVstCyclePosValid | kVstTimeSigValid
};
std::unordered_map<std::string, bool> can_do_map = {
{"sendVstEvents", true}, {"sendVstMidiEvent", true}, {"sendVstTimeInfo", true}, {"receiveVstEvents", false},
@ -27,8 +29,9 @@ std::shared_ptr<dynamic_library> get_vst2_library(const std::string& path) {
}
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);
if (can_do_map.contains(can_do))
return can_do_map[can_do];
ensuref(can_do_map.contains(can_do), "Unknown can do: {}", can_do);
return false;
}
@ -67,7 +70,7 @@ VstIntPtr vst_master_callback(AEffect* effect, VstInt32 opcode, VstInt32 index,
return 1;
}
case audioMasterGetTime :
return (VstIntPtr) &vst2_time;
return (VstIntPtr)&vst2_time;
case audioMasterIdle :
// UE_LOG(LogTemp, Log, TEXT("Plugin Idle"));
return 1;
@ -123,25 +126,39 @@ VstIntPtr vst_master_callback(AEffect* effect, VstInt32 opcode, VstInt32 index,
return 1;
}
typedef AEffect*(*vst_plugin_entry_proc)(audioMasterCallback AudioMaster);
using vst_plugin_entry_proc = AEffect*(audioMasterCallback);
vst2_events::vst_events_handle::vst_events_handle(const std::vector<VstMidiEvent>& events) {
const auto num_events = events.size();
const auto event_size = num_events * sizeof(VstEvent*) + sizeof(VstEvents);
const auto buffer = static_cast<VstEvents*>(std::malloc(event_size));
std::memset(buffer, 0, event_size);
buffer->numEvents = num_events;
for (int i = 0; i < num_events; ++i) {
buffer->events[i] = (VstEvent*)&events[i];
}
data_ = buffer;
}
void vst2_events::push(const smf::MidiEvent& event, uint32_t frame_delta) {
// if (!event.isNote())
// return;
VstMidiEvent midi_event{};
memset(&midi_event, 0, sizeof(VstMidiEvent));
midi_event.type = kVstMidiType;
midi_event.byteSize = sizeof(VstMidiEvent);
midi_event.deltaFrames = frame_delta;
midi_event.midiData[0] = event[0];
midi_event.midiData[1] = event[1];
midi_event.midiData[2] = event[2];
midi_events_.push_back(midi_event);
midi_event.midiData[0] = event.getP0();
midi_event.midiData[1] = event.getP1();
midi_event.midiData[2] = event.getP2();
midi_event.noteOffVelocity = event.isNoteOff() ? event.getVelocity() : 0;
vst_events_.numEvents = static_cast<VstInt32>(midi_events_.size());
vst_events_.events[0] = (VstEvent*)midi_events_.data();
midi_events_.push_back(midi_event);
}
void vst2_events::clear() {
midi_events_.clear();
vst_events_.numEvents = 0;
}
vst2_plugin_host::vst2_plugin_host() : plugin_host(type) {
@ -161,7 +178,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;
@ -224,7 +241,11 @@ void vst2_plugin_host::update_channel_node_name() {
}
void vst2_plugin_host::process(uint32_t frame_num) {
// TODO send midi
if (!events_.empty()) {
auto event_handle = events_.flush();
dispatch(effProcessEvents, 0, 0, event_handle);
}
#if USE_DOUBLE_SAMPLE
effect_->processDoubleReplacing(effect_, channel->get_input_headers(), channel->get_output_headers(), static_cast<VstInt32>(frame_num));
#else
@ -240,7 +261,6 @@ void vst2_plugin_host::process(uint32_t frame_num) {
void vst2_plugin_host::push_midi(const smf::MidiEvent& event, uint32_t frame_delta) {
events_.push(event, frame_delta);
dispatch(effProcessEvents, 0, 0, events_.get());
}
void vst2_plugin_host::idle_editor() {

View File

@ -6,13 +6,35 @@
class vst2_events {
public:
struct vst_events_handle {
public:
vst_events_handle(const std::vector<VstMidiEvent>& events);
vst_events_handle(const vst_events_handle&) = delete;
vst_events_handle(vst_events_handle&& other) noexcept : data_(other.data_) {
other.data_ = nullptr;
}
~vst_events_handle() {
free(data_);
}
operator VstEvents*() {
return data_;
}
private:
VstEvents* data_;
};
void push(const smf::MidiEvent& event, uint32_t frame_delta);
void clear();
[[nodiscard]] auto get() -> VstEvents* {
return &vst_events_;
[[nodiscard]] auto flush() -> vst_events_handle {
vst_events_handle out { midi_events_ };
return out;
}
[[nodiscard]] auto empty() const -> bool {
return midi_events_.empty();
}
private:
VstEvents vst_events_;
std::vector<VstMidiEvent> midi_events_;
};

57
core/misc/core_assert.h Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#include "spdlog/spdlog.h"
#include <cassert>
// 定义 check 宏
#define check(expr) \
do { \
if (!(expr)) { \
spdlog::critical("Check failed: {} in file {} at line {}", #expr, __FILE__, __LINE__); \
assert(expr); \
} \
} while (0)
// 定义 checkf 宏
#define checkf(expr, msg, ...) \
do { \
if (!(expr)) { \
spdlog::critical("Check failed: {} in file {} at line {}. " msg, #expr, __FILE__, __LINE__, __VA_ARGS__); \
assert(expr); \
} \
} while (0)
// 定义 ensure 宏
#if BUILD_DEBUG
#define ensure(expr) \
do { \
if (!(expr)) { \
spdlog::error("Ensure failed: {} in file {} at line {}", #expr, __FILE__, __LINE__); \
assert(expr); \
} \
} while (0)
#else
#define ensure(expr) \
do { \
if (!(expr)) { \
spdlog::error("Ensure condition failed: {} in file {} at line {}", #expr, __FILE__, __LINE__); \
} \
} while (0)
#endif
// 定义 ensuref 宏
#if BUILD_DEBUG
#define ensuref(expr, msg, ...) \
do { \
if (!(expr)) { \
spdlog::error("Ensure failed: {} in file {} at line {}. " msg, #expr, __FILE__, __LINE__, __VA_ARGS__); \
assert(expr); \
} \
} while (0)
#else
#define ensuref(expr, msg, ...) \
do { \
if (!(expr)) { \
spdlog::error("Ensure condition failed: {} in file {} at line {}. " msg, #expr, __FILE__, __LINE__, __VA_ARGS__); \
} \
} while (0)
#endif

54
core/misc/core_math.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
template <typename Type, size_t N>
constexpr int numElementsInArray(Type (&)[N]) noexcept { return N; }
/** Remaps a value from a source range to a target range. */
template <typename Type>
auto jmap(Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax) -> Type
{
check(sourceRangeMax != sourceRangeMin); // mapping from a range of zero will produce NaN!
return targetRangeMin + ((targetRangeMax - targetRangeMin) * (sourceValue - sourceRangeMin)) / (sourceRangeMax -
sourceRangeMin);
}
template <typename FloatType>
auto RoundToInt(const FloatType value) noexcept -> int
{
#ifdef __INTEL_COMPILER
#pragma float_control (precise, on, push)
#endif
union
{
int asInt[2];
double asDouble;
} n;
n.asDouble = static_cast<double>(value) + 6755399441055744.0;
#if JUCE_BIG_ENDIAN
return n.asInt [1];
#else
return n.asInt[0];
#endif
}
/** Returns true if a value is at least zero, and also below a specified upper limit.
This is basically a quicker way to write:
@code valueToTest >= 0 && valueToTest < upperLimit
@endcode
*/
template <typename Type1, typename Type2>
auto IsPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept -> bool
{
check(Type1() <= static_cast<Type1> (upperLimit));
// makes no sense to call this if the upper limit is itself below zero..
return Type1() <= valueToTest && valueToTest < static_cast<Type1>(upperLimit);
}
template <typename Type>
auto IsPositiveAndBelow(int valueToTest, Type upperLimit) noexcept -> bool
{
check(upperLimit >= 0); // makes no sense to call this if the upper limit is itself below zero..
return static_cast<unsigned int>(valueToTest) < static_cast<unsigned int>(upperLimit);
}

36
core/misc/core_str.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
// 定义 TCHAR 类型
#ifdef UNICODE
using char_t = wchar_t;
#define TEXT(x) L##x
#else
using char_t = char;
#define TEXT(x) x
#endif
inline auto ANSI_TO_TCHAR(const char* ansi_str) -> const char_t* {
#ifdef UNICODE
// 如果 TCHAR 是 wchar_t需要进行转换
size_t len = strlen(ansiStr) + 1;
std::vector<wchar_t> wstr(len);
mbstowcs(wstr.data(), ansiStr, len);
return wstr.data();
#else
// 如果 TCHAR 是 char直接返回原始字符串
return ansi_str;
#endif
}
inline auto TCHAR_TO_ANSI(const char_t* tchar_str) -> const char* {
#ifdef UNICODE
// 如果 TCHAR 是 wchar_t需要进行转换
size_t len = wcslen(tcharStr) + 1;
std::vector<char> ansiStr(len);
wcstombs(ansiStr.data(), tcharStr, len);
return ansiStr.data();
#else
// 如果 TCHAR 是 char直接返回原始字符串
return tchar_str;
#endif
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <memory>
#include <vector>
#include <bits/ranges_algo.h>
class memory_pool {
public:
@ -66,22 +67,19 @@ template<class T>
class obj_mempool {
public:
template<typename ...Args>
static auto alloc(Args&&... args) -> T* {
auto obj = static_cast<T*>(pool_.allocate());
static auto construct(Args&&... args) -> T* {
auto obj = alloc();
new (obj) T(std::forward<Args>(args)...);
objs_.push_back(obj);
return obj;
}
static auto alloc() -> T* {
auto obj = static_cast<T*>(pool_.allocate());
static auto construct() -> T* {
auto obj = alloc();
new (obj) T();
objs_.push_back(obj);
return obj;
}
static void free(T* p) {
p->~T();
pool_.deallocate(p);
objs_.erase(std::ranges::remove(objs, p));
deallocate(p);
}
static void free_all() {
for (auto obj : objs_) {
@ -103,6 +101,17 @@ public:
}
return false;
}
static auto alloc() -> T* {
T* p = static_cast<T*>(pool_.allocate());
objs_.push_back(p);
return p;
}
static void deallocate(T* p) {
pool_.deallocate(p);
auto e = std::ranges::remove(objs_, p);
objs_.erase(e.begin(), e.end());
}
private:
inline static auto pool_ = memory_pool(sizeof(T), 64);
inline static std::vector<T*> objs_;
@ -110,12 +119,11 @@ private:
template<class T>
class pool_obj {
inline static auto pool = memory_pool(sizeof(T), 64);
public:
auto operator new(size_t size) -> void* {
return pool.allocate();
return obj_mempool<T>::alloc();
}
void operator delete(void* p) {
pool.deallocate(p);
obj_mempool<T>::deallocate(static_cast<T*>(p));
}
};

View File

@ -1,12 +1,12 @@
#pragma once
#include <assert.h>
#include <cassert>
/**
* A smart pointer to an object which implements AddRef/Release.
*/
template<typename ReferencedType>
class ref_count_ptr {
typedef ReferencedType* reference_type;
using reference_type = ReferencedType *;
public:
ref_count_ptr(): reference_(nullptr) {

View File

@ -1,5 +1,3 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
namespace scope_exit_support {

View File

@ -4,26 +4,25 @@
midi_tick::midi_tick(const audio_frame& in_frame) {
auto bpm = g_midi_sequencer.get_bpm();
auto tpq = g_midi_sequencer.get_tpq();
auto tpq = midi_sequencer::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);
auto midi_tick::to_audio_frame(int32_t tpq) const -> audio_frame {
return audio_frame(*this, tpq);
}
audio_frame::audio_frame(const midi_tick& in_tick) {
audio_frame::audio_frame(const midi_tick& in_tick, int32_t tpq) {
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 {
midi_tick audio_frame::to_midi_tick(int32_t tpq) const {
return midi_tick(*this);
}

View File

@ -12,7 +12,7 @@ public:
}
explicit midi_tick(const audio_frame& in_frame);
[[nodiscard]] auto to_audio_frame() const -> audio_frame;
[[nodiscard]] auto to_audio_frame(int32_t tpq) const -> audio_frame;
[[nodiscard]] auto get_ticks() const -> int64_t {
return ticks;
}
@ -37,9 +37,9 @@ public:
audio_frame(int64_t in_frames) : frames(in_frames) {
}
explicit audio_frame(const midi_tick& in_tick);
explicit audio_frame(const midi_tick& in_tick, int32_t tpq);
[[nodiscard]] auto to_midi_tick() const -> midi_tick;
[[nodiscard]] auto to_midi_tick(int32_t tpq) const -> midi_tick;
[[nodiscard]] auto get_frames() const -> int64_t {
return frames;
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <functional>
#include "extern.h"
#include "misc/mempool.h"
class CORE_API thread_message {
public:
@ -9,7 +10,7 @@ public:
virtual void execute() = 0;
};
class CORE_API lamba_thread_message : public thread_message {
class CORE_API lamba_thread_message : public thread_message, public pool_obj<lamba_thread_message> {
public:
lamba_thread_message(const std::function<void()>& in_func) : function(in_func) {

View File

@ -6,7 +6,7 @@ void thread_message_hub::process_messages() {
while (!messages_.empty()) {
lamba_thread_message* message = messages_.front();
message->execute();
obj_mempool<lamba_thread_message>::free(message);
delete message;
messages_.pop();
}
}

View File

@ -2,7 +2,6 @@
#include <queue>
#include "../misc/mempool.h"
#include "thread_message.h"
class CORE_API thread_message_hub {
@ -18,7 +17,7 @@ public:
// push_message(message);
// }
void push_message(const std::function<void()>& func) {
auto* message = obj_mempool<lamba_thread_message>::alloc(func);
auto* message = new lamba_thread_message(func);
push_message(message);
}
void process_messages();