延迟补偿
This commit is contained in:
@@ -22,10 +22,10 @@
|
||||
template<typename SampleType>
|
||||
class circular_audio_buffer {
|
||||
private:
|
||||
std::vector<SampleType> InternalBuffer;
|
||||
uint32_t Capacity;
|
||||
std::atomic<int32_t> ReadCounter;
|
||||
std::atomic<int32_t> WriteCounter;
|
||||
std::vector<SampleType> internal_buffer_;
|
||||
uint32_t capacity_;
|
||||
std::atomic<int32_t> read_counter_;
|
||||
std::atomic<int32_t> write_counter_;
|
||||
|
||||
public:
|
||||
circular_audio_buffer() {
|
||||
@@ -37,171 +37,171 @@ public:
|
||||
}
|
||||
|
||||
circular_audio_buffer& operator=(const circular_audio_buffer& InOther) {
|
||||
InternalBuffer = InOther.InternalBuffer;
|
||||
Capacity = InOther.Capacity;
|
||||
ReadCounter.store(InOther.ReadCounter);
|
||||
WriteCounter.store(InOther.WriteCounter);
|
||||
internal_buffer_ = InOther.internal_buffer_;
|
||||
capacity_ = InOther.capacity_;
|
||||
read_counter_.store(InOther.read_counter_);
|
||||
write_counter_.store(InOther.write_counter_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
explicit circular_audio_buffer(uint32_t InCapacity) {
|
||||
set_capacity(InCapacity);
|
||||
explicit circular_audio_buffer(uint32_t in_capacity) {
|
||||
set_capacity(in_capacity);
|
||||
}
|
||||
|
||||
// move
|
||||
circular_audio_buffer(circular_audio_buffer&& InOther) noexcept {
|
||||
InternalBuffer = std::move(InOther.InternalBuffer);
|
||||
Capacity = InOther.Capacity;
|
||||
ReadCounter.store(InOther.ReadCounter);
|
||||
WriteCounter.store(InOther.WriteCounter);
|
||||
circular_audio_buffer(circular_audio_buffer&& in_other) noexcept {
|
||||
internal_buffer_ = std::move(in_other.internal_buffer_);
|
||||
capacity_ = in_other.capacity_;
|
||||
read_counter_.store(in_other.read_counter_);
|
||||
write_counter_.store(in_other.write_counter_);
|
||||
|
||||
InOther.Capacity = 0;
|
||||
InOther.ReadCounter = 0;
|
||||
InOther.WriteCounter = 0;
|
||||
in_other.capacity_ = 0;
|
||||
in_other.read_counter_ = 0;
|
||||
in_other.write_counter_ = 0;
|
||||
}
|
||||
|
||||
void reset(uint32_t InCapacity = 0) {
|
||||
set_capacity(InCapacity);
|
||||
void reset(uint32_t in_capacity = 0) {
|
||||
set_capacity(in_capacity);
|
||||
}
|
||||
|
||||
void set_capacity(uint32_t InCapacity) {
|
||||
if (InCapacity >= 2147483647) { spdlog::error("Max capacity for this buffer is 2,147,483,647 samples. Otherwise our index arithmetic will not work."); throw std::runtime_error(fmt::format("Max capacity for this buffer is 2,147,483,647 samples. Otherwise our index arithmetic will not work.")); };
|
||||
void set_capacity(uint32_t in_capacity) {
|
||||
if (in_capacity >= 2147483647) { spdlog::error("Max capacity for this buffer is 2,147,483,647 samples. Otherwise our index arithmetic will not work."); throw std::runtime_error(fmt::format("Max capacity for this buffer is 2,147,483,647 samples. Otherwise our index arithmetic will not work.")); };
|
||||
|
||||
Capacity = InCapacity + 1;
|
||||
ReadCounter = 0;
|
||||
WriteCounter = 0;
|
||||
InternalBuffer.clear();
|
||||
InternalBuffer.resize(Capacity);
|
||||
capacity_ = in_capacity + 1;
|
||||
read_counter_ = 0;
|
||||
write_counter_ = 0;
|
||||
internal_buffer_.clear();
|
||||
internal_buffer_.resize(capacity_);
|
||||
}
|
||||
|
||||
/** Reserve capacity.
|
||||
*
|
||||
* @param InMinimumCapacity - Minimum capacity of circular buffer.
|
||||
* @param bRetainExistingSamples - If true, existing samples in the buffer will be retained. If false, they are discarded.
|
||||
* @param in_minimum_capacity - Minimum capacity of circular buffer.
|
||||
* @param retain_existing_samples - If true, existing samples in the buffer will be retained. If false, they are discarded.
|
||||
*/
|
||||
void reserve(uint32_t InMinimumCapacity, bool bRetainExistingSamples) {
|
||||
if (Capacity <= InMinimumCapacity) {
|
||||
uint32_t NewCapacity = InMinimumCapacity + 1;
|
||||
void reserve(uint32_t in_minimum_capacity, bool retain_existing_samples) {
|
||||
if (capacity_ <= in_minimum_capacity) {
|
||||
uint32_t new_capacity = in_minimum_capacity + 1;
|
||||
|
||||
checkf(NewCapacity < (uint32_t)INT32_MAX,
|
||||
"Max capacity overflow. Requested {}. Maximum allowed {}", NewCapacity,
|
||||
INT32_MAX);
|
||||
checkf(new_capacity < (uint32_t)INT32_MAX,
|
||||
"Max capacity overflow. Requested {}. Maximum allowed {}", new_capacity,
|
||||
INT32_MAX);
|
||||
|
||||
uint32_t NumToAdd = NewCapacity - Capacity;
|
||||
InternalBuffer.AddZeroed(NumToAdd);
|
||||
Capacity = NewCapacity;
|
||||
uint32_t num_to_add = new_capacity - capacity_;
|
||||
internal_buffer_.AddZeroed(num_to_add);
|
||||
capacity_ = new_capacity;
|
||||
}
|
||||
|
||||
if (!bRetainExistingSamples) {
|
||||
ReadCounter = 0;
|
||||
WriteCounter = 0;
|
||||
if (!retain_existing_samples) {
|
||||
read_counter_ = 0;
|
||||
write_counter_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Push an array of values into circular buffer. */
|
||||
int32_t push(const std::vector<SampleType>& InBuffer) {
|
||||
return push(InBuffer.data(), InBuffer.size());
|
||||
int32_t push(const std::vector<SampleType>& in_buffer) {
|
||||
return push(in_buffer.data(), in_buffer.size());
|
||||
}
|
||||
|
||||
// Pushes some amount of samples into this circular buffer.
|
||||
// Returns the amount of samples written.
|
||||
// This can only be used for trivially copyable types.
|
||||
int32_t push(const SampleType* InBuffer, uint32_t NumSamples) {
|
||||
SampleType* DestBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
int32_t push(const SampleType* in_buffer, uint32_t num_samples) {
|
||||
SampleType* dest_buffer = internal_buffer_.data();
|
||||
const uint32_t read_index = read_counter_;
|
||||
const uint32_t write_index = write_counter_;
|
||||
|
||||
int32_t NumToCopy = std::min<int32_t>(NumSamples, remainder());
|
||||
const int32_t NumToWrite = std::min<int32_t>(NumToCopy, Capacity - WriteIndex);
|
||||
int32_t num_to_copy = std::min<int32_t>(num_samples, remainder());
|
||||
const int32_t num_to_write = std::min<int32_t>(num_to_copy, capacity_ - write_index);
|
||||
|
||||
memcpy(&DestBuffer[WriteIndex], InBuffer, NumToWrite * sizeof(SampleType));
|
||||
memcpy(&DestBuffer[0], &InBuffer[NumToWrite], (NumToCopy - NumToWrite) * sizeof(SampleType));
|
||||
memcpy(&dest_buffer[write_index], in_buffer, num_to_write * sizeof(SampleType));
|
||||
memcpy(&dest_buffer[0], &in_buffer[num_to_write], (num_to_copy - num_to_write) * sizeof(SampleType));
|
||||
|
||||
WriteCounter = (WriteIndex + NumToCopy) % Capacity;
|
||||
write_counter_ = (write_index + num_to_copy) % capacity_;
|
||||
|
||||
return NumToCopy;
|
||||
return num_to_copy;
|
||||
}
|
||||
|
||||
// Pushes some amount of zeros into the circular buffer.
|
||||
// Useful when acting as a blocked, mono/interleaved delay line
|
||||
int32_t push_zeros(uint32_t NumSamplesOfZeros) {
|
||||
SampleType* DestBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
int32_t push_zeros(uint32_t num_samples_of_zeros) {
|
||||
SampleType* dest_buffer = internal_buffer_.data();
|
||||
const uint32_t read_index = read_counter_;
|
||||
const uint32_t write_index = write_counter_;
|
||||
|
||||
int32_t NumToZeroEnd = std::min<int32_t>(NumSamplesOfZeros, remainder());
|
||||
const int32_t NumToZeroBegin = std::min<int32_t>(NumToZeroEnd, Capacity - WriteIndex);
|
||||
int32_t num_to_zero_end = std::min<int32_t>(num_samples_of_zeros, remainder());
|
||||
const int32_t num_to_zero_begin = std::min<int32_t>(num_to_zero_end, capacity_ - write_index);
|
||||
|
||||
memset(&DestBuffer[WriteIndex], 0, NumToZeroBegin * sizeof(SampleType));
|
||||
memset(&DestBuffer[0], 0, (NumToZeroEnd - NumToZeroBegin) * sizeof(SampleType));
|
||||
memset(&dest_buffer[write_index], 0, num_to_zero_begin * sizeof(SampleType));
|
||||
memset(&dest_buffer[0], 0, (num_to_zero_end - num_to_zero_begin) * sizeof(SampleType));
|
||||
|
||||
WriteCounter = (WriteIndex + NumToZeroEnd) % Capacity;
|
||||
write_counter_ = (write_index + num_to_zero_end) % capacity_;
|
||||
|
||||
return NumToZeroEnd;
|
||||
return num_to_zero_end;
|
||||
}
|
||||
|
||||
// Push a single sample onto this buffer.
|
||||
// Returns false if the buffer is full.
|
||||
bool push(const SampleType& InElement) {
|
||||
bool push(const SampleType& in_element) {
|
||||
if (remainder() == 0) {
|
||||
return false;
|
||||
} else {
|
||||
SampleType* DestBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
SampleType* dest_buffer = internal_buffer_.data();
|
||||
const uint32_t read_index = read_counter_;
|
||||
const uint32_t write_index = write_counter_;
|
||||
|
||||
DestBuffer[WriteIndex] = InElement;
|
||||
dest_buffer[write_index] = in_element;
|
||||
|
||||
WriteCounter = (WriteIndex + 1) % Capacity;
|
||||
write_counter_ = (write_index + 1) % capacity_;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool push(SampleType&& InElement) {
|
||||
bool push(SampleType&& in_element) {
|
||||
if (remainder() == 0) {
|
||||
return false;
|
||||
} else {
|
||||
SampleType* DestBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
SampleType* dest_buffer = internal_buffer_.data();
|
||||
const uint32_t read_index = read_counter_;
|
||||
const uint32_t write_counter = write_counter_;
|
||||
|
||||
DestBuffer[WriteIndex] = MoveTemp(InElement);
|
||||
dest_buffer[write_counter] = MoveTemp(in_element);
|
||||
|
||||
WriteCounter = (WriteIndex + 1) % Capacity;
|
||||
write_counter_ = (write_counter + 1) % capacity_;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Same as Pop(), but does not increment the read counter.
|
||||
int32_t peek(SampleType* OutBuffer, uint32_t NumSamples) const {
|
||||
const SampleType* SrcBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
int32_t peek(SampleType* out_buffer, uint32_t num_samples) const {
|
||||
const SampleType* src_buffer = internal_buffer_.data();
|
||||
const uint32_t read_index = read_counter_;
|
||||
const uint32_t write_index = write_counter_;
|
||||
|
||||
int32_t NumToCopy = std::min<int32_t>(NumSamples, num());
|
||||
int32_t num_to_copy = std::min<int32_t>(num_samples, num());
|
||||
|
||||
const int32_t NumRead = std::min<int32_t>(NumToCopy, Capacity - ReadIndex);
|
||||
memcpy(OutBuffer, &SrcBuffer[ReadIndex], NumRead * sizeof(SampleType));
|
||||
const int32_t num_read = std::min<int32_t>(num_to_copy, capacity_ - read_index);
|
||||
memcpy(out_buffer, &src_buffer[read_index], num_read * sizeof(SampleType));
|
||||
|
||||
memcpy(&OutBuffer[NumRead], &SrcBuffer[0], (NumToCopy - NumRead) * sizeof(SampleType));
|
||||
memcpy(&out_buffer[num_read], &src_buffer[0], (num_to_copy - num_read) * sizeof(SampleType));
|
||||
|
||||
check(NumSamples < ((uint32_t)INT32_MAX));
|
||||
check(num_samples < ((uint32_t)INT32_MAX));
|
||||
|
||||
return NumToCopy;
|
||||
return num_to_copy;
|
||||
}
|
||||
|
||||
// Peeks a single element.
|
||||
// returns false if the element is empty.
|
||||
bool peek(SampleType& OutElement) const {
|
||||
bool peek(SampleType& out_element) const {
|
||||
if (num() == 0) {
|
||||
return false;
|
||||
} else {
|
||||
SampleType* SrcBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
SampleType* src_buffer = internal_buffer_.data();
|
||||
const uint32_t read_index = read_counter_;
|
||||
|
||||
OutElement = SrcBuffer[ReadIndex];
|
||||
out_element = src_buffer[read_index];
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -209,25 +209,25 @@ public:
|
||||
|
||||
// Pops some amount of samples into this circular buffer.
|
||||
// Returns the amount of samples read.
|
||||
int32_t pop(SampleType* OutBuffer, uint32_t NumSamples) {
|
||||
int32_t NumSamplesRead = peek(OutBuffer, NumSamples);
|
||||
check(NumSamples < ((uint32_t)INT32_MAX));
|
||||
int32_t pop(SampleType* out_buffer, uint32_t num_samples) {
|
||||
int32_t num_samples_read = peek(out_buffer, num_samples);
|
||||
check(num_samples < ((uint32_t)INT32_MAX));
|
||||
|
||||
ReadCounter = (ReadCounter + NumSamplesRead) % Capacity;
|
||||
read_counter_ = (read_counter_ + num_samples_read) % capacity_;
|
||||
|
||||
return NumSamplesRead;
|
||||
return num_samples_read;
|
||||
}
|
||||
|
||||
// Pops some amount of samples into this circular buffer.
|
||||
// Returns the amount of samples read.
|
||||
int32_t pop(uint32_t NumSamples) {
|
||||
check(NumSamples < ((uint32_t)INT32_MAX));
|
||||
int32_t pop(uint32_t num_samples) {
|
||||
check(num_samples < ((uint32_t)INT32_MAX));
|
||||
|
||||
int32_t NumSamplesRead = std::min<int32_t>(NumSamples, num());
|
||||
int32_t num_samples_read = std::min<int32_t>(num_samples, num());
|
||||
|
||||
ReadCounter = (ReadCounter + NumSamplesRead) % Capacity;
|
||||
read_counter_ = (read_counter_ + num_samples_read) % capacity_;
|
||||
|
||||
return NumSamplesRead;
|
||||
return num_samples_read;
|
||||
}
|
||||
|
||||
// Pops a single element.
|
||||
@@ -236,55 +236,63 @@ public:
|
||||
// Calling this when the buffer is empty is considered a fatal error.
|
||||
check(Num() > 0);
|
||||
|
||||
SampleType* SrcBuffer = InternalBuffer.data();
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
SampleType* src_buffer = internal_buffer_.data();
|
||||
const uint32_t read_index = read_counter_;
|
||||
|
||||
SampleType PoppedValue = std::move(InternalBuffer[ReadIndex]);
|
||||
ReadCounter = (ReadCounter + 1) % Capacity;
|
||||
return PoppedValue;
|
||||
SampleType popped_value = std::move(internal_buffer_[read_index]);
|
||||
read_counter_ = (read_counter_ + 1) % capacity_;
|
||||
return popped_value;
|
||||
}
|
||||
|
||||
SampleType* get_header() {
|
||||
// Calling this when the buffer is empty is considered a fatal error.
|
||||
check(Num() > 0);
|
||||
|
||||
// 根据read_counter_获取当前的header
|
||||
return &internal_buffer_[read_counter_];
|
||||
}
|
||||
|
||||
// When called, seeks the read or write cursor to only retain either the NumSamples latest data
|
||||
// (if bRetainOldestSamples is false) or the NumSamples oldest data (if bRetainOldestSamples is true)
|
||||
// in the buffer. Cannot be used to increase the capacity of this buffer.
|
||||
void set_num(uint32_t NumSamples, bool bRetainOldestSamples = false) {
|
||||
check(NumSamples < Capacity);
|
||||
void set_num(uint32_t num_samples, bool retain_oldest_samples = false) {
|
||||
check(num_samples < capacity_);
|
||||
|
||||
if (bRetainOldestSamples) {
|
||||
WriteCounter = (ReadCounter + NumSamples) % Capacity;
|
||||
if (retain_oldest_samples) {
|
||||
write_counter_ = (read_counter_ + num_samples) % capacity_;
|
||||
} else {
|
||||
int64_t ReadCounterNum = ((int32_t) WriteCounter) - ((int32_t) NumSamples);
|
||||
if (ReadCounterNum < 0) {
|
||||
ReadCounterNum = Capacity + ReadCounterNum;
|
||||
int64_t read_counter_num = ((int32_t) write_counter_) - ((int32_t) num_samples);
|
||||
if (read_counter_num < 0) {
|
||||
read_counter_num = capacity_ + read_counter_num;
|
||||
}
|
||||
|
||||
ReadCounter = ReadCounterNum;
|
||||
read_counter_ = read_counter_num;
|
||||
}
|
||||
}
|
||||
|
||||
// Get number of samples that can be popped off of the buffer.
|
||||
uint32_t num() const {
|
||||
const int32_t ReadIndex = ReadCounter;
|
||||
const int32_t WriteIndex = WriteCounter;
|
||||
const int32_t read_index = read_counter_;
|
||||
const int32_t write_index = write_counter_;
|
||||
|
||||
if (WriteIndex >= ReadIndex) {
|
||||
return WriteIndex - ReadIndex;
|
||||
if (write_index >= read_index) {
|
||||
return write_index - read_index;
|
||||
} else {
|
||||
return Capacity - ReadIndex + WriteIndex;
|
||||
return capacity_ - read_index + write_index;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current capacity of the buffer
|
||||
uint32_t get_capacity() const {
|
||||
return Capacity;
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
// Get number of samples that can be pushed onto the buffer before it is full.
|
||||
uint32_t remainder() const {
|
||||
const uint32_t ReadIndex = ReadCounter;
|
||||
const uint32_t WriteIndex = WriteCounter;
|
||||
const uint32_t read_index = read_counter_;
|
||||
const uint32_t write_index = write_counter_;
|
||||
|
||||
return (Capacity - 1 - WriteIndex + ReadIndex) % Capacity;
|
||||
return (capacity_ - 1 - write_index + read_index) % capacity_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -18,9 +18,13 @@ latency_compensator::~latency_compensator() {
|
||||
void latency_compensator::init() {
|
||||
const uint32_t channel_count = g_audio_device_manager.get_output_channel_count();
|
||||
internal_buffer.resize(channel_count);
|
||||
ui_buffers.resize(channel_count);
|
||||
|
||||
uint32_t block_size = g_audio_device_manager.get_buffer_size();
|
||||
resize(block_size);
|
||||
for (auto& buffer : ui_buffers) {
|
||||
buffer.set_capacity(block_size * 2);
|
||||
}
|
||||
}
|
||||
|
||||
void latency_compensator::set_latency(int32_t in_latency_offset) {
|
||||
@@ -51,30 +55,32 @@ void latency_compensator::push(audio_buffer& in_buffer) {
|
||||
void latency_compensator::pop(uint64_t delta_sample, circular_buffer_vector_type& out_buffer) {
|
||||
std::vector<sample_t> temp_buffer(delta_sample);
|
||||
|
||||
for (auto buffer : std::views::zip(internal_buffer, out_buffer)) {
|
||||
auto& [internal, target_buffer] = buffer;
|
||||
for (auto buffer : std::views::zip(internal_buffer, ui_buffers, out_buffer)) {
|
||||
auto& [internal, ui_buffer, target_buffer] = buffer;
|
||||
internal.pop(temp_buffer.data(), delta_sample);
|
||||
target_buffer.push(temp_buffer);
|
||||
ui_buffer.push(temp_buffer);
|
||||
|
||||
memset(temp_buffer.data(), 0, delta_sample * sizeof(sample_t));
|
||||
}
|
||||
}
|
||||
|
||||
void latency_compensator::pop(uint64_t delta_smaple, audio_buffer& out_buffer) {
|
||||
for (auto buffer : std::views::zip(internal_buffer, out_buffer.get_headers_vector())) {
|
||||
auto& [internal, target_buffer] = buffer;
|
||||
for (auto buffer : std::views::zip(internal_buffer, ui_buffers, out_buffer.get_headers_vector())) {
|
||||
auto& [internal, ui_buffer, target_buffer] = buffer;
|
||||
internal.pop(target_buffer, delta_smaple);
|
||||
ui_buffer.push(target_buffer, delta_smaple);
|
||||
}
|
||||
}
|
||||
|
||||
void latency_compensator::update_buffer(int32_t min_latency, int32_t max_latency) {
|
||||
// 根据全局最大延迟计算需要的buffer大小
|
||||
const uint32_t block_size = g_audio_device_manager.get_buffer_size();
|
||||
const auto& buffer_size = get_real_latency() - max_latency;
|
||||
const auto& buffer_size = std::max(0, max_latency - get_real_latency());
|
||||
|
||||
const uint32_t block_num = buffer_size / block_size + 1;
|
||||
const int32_t block_num = buffer_size / block_size + 1;
|
||||
resize(block_num * block_size);
|
||||
|
||||
const auto& buffer_latency = (get_real_latency() - min_latency) + max_latency; // 计算需要的buffer大小
|
||||
const auto& buffer_latency = max_latency - (get_real_latency() - min_latency); // 计算需要的buffer大小
|
||||
push_zeros(buffer_latency);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "audio/misc/circular_audio_buffer.h"
|
||||
#include "misc/delegates.h"
|
||||
|
||||
inline static multicast_delegate<> on_latency_offset_changed;
|
||||
inline multicast_delegate<> on_latency_offset_changed;
|
||||
|
||||
class latency_compensator {
|
||||
public:
|
||||
@@ -33,6 +33,8 @@ public:
|
||||
[[nodiscard]] int32_t get_real_latency() const { return latency_ + user_latency_; }
|
||||
|
||||
[[nodiscard]] circular_buffer_vector_type& get_internal_buffer() { return internal_buffer; }
|
||||
|
||||
circular_buffer_vector_type ui_buffers;
|
||||
protected:
|
||||
void resize(uint32_t block_size) {
|
||||
for (auto& channel : internal_buffer) {
|
||||
|
||||
@@ -235,9 +235,9 @@ void mixer::on_mixer_latency_changed() {
|
||||
max_latency = std::max<int32_t>(max_latency, track->get_real_latency());
|
||||
min_latency = std::min<int32_t>(min_latency, track->get_real_latency());
|
||||
}
|
||||
int32_t latency = max_latency - min_latency;
|
||||
|
||||
for (mixer_track* track : tracks_) {
|
||||
for (size_t i = 1; i < tracks_.size(); i++) {
|
||||
mixer_track* track = tracks_[i];
|
||||
track->compensator_.update_buffer(min_latency, max_latency);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@ void mixer_track::init() {
|
||||
const uint32_t channel_count = g_audio_device_manager.get_output_channel_count();
|
||||
buffer_.resize(channel_count, g_audio_device_manager.get_buffer_size());
|
||||
temp_mix_buffer_.resize(buffer_.get_num_channels(), g_audio_device_manager.get_buffer_size());
|
||||
for (int i = 0; i < channel_count; ++i) {
|
||||
ui_buffers->emplace_back(g_audio_device_manager.get_buffer_size() * 2);
|
||||
}
|
||||
|
||||
channel_interface_ = new mixer_channel_interface(this);
|
||||
compensator_.init();
|
||||
@@ -74,11 +71,6 @@ void mixer_track::process(uint32_t in_frames) {
|
||||
|
||||
void mixer_track::post_process(uint32_t in_frames) {
|
||||
buffer_.multiple(get_volume());
|
||||
// TODO ui_buffer需要在compensator后面
|
||||
for (int i = 0; i < buffer_.get_num_channels(); ++i) {
|
||||
auto& ui_buffer = (*ui_buffers)[i];
|
||||
ui_buffer.push(buffer_.get_headers()[i], in_frames);
|
||||
}
|
||||
}
|
||||
|
||||
void mixer_track::on_latency_changed(int32_t in_latency) {
|
||||
@@ -102,7 +94,7 @@ std::string instrument_track::get_name() const {
|
||||
}
|
||||
|
||||
void instrument_track::on_latency_changed(int32_t in_latency) {
|
||||
int32_t latency = in_latency;
|
||||
int32_t latency = 0;
|
||||
for (const auto effect: effects) {
|
||||
latency += effect->get_latency();
|
||||
}
|
||||
|
||||
@@ -28,9 +28,7 @@ struct mixer_track_link {
|
||||
class CORE_API mixer_track {
|
||||
friend class mixer;
|
||||
public:
|
||||
explicit mixer_track(mixer_track_type in_type) : type_(in_type), id_(gen_uid()) {
|
||||
ui_buffers = std::make_shared<circular_buffer_vector_type>();
|
||||
}
|
||||
explicit mixer_track(mixer_track_type in_type) : type_(in_type), id_(gen_uid()) {}
|
||||
[[nodiscard]] uint64_t get_uid() const { return id_; }
|
||||
virtual ~mixer_track();
|
||||
|
||||
@@ -61,6 +59,7 @@ public:
|
||||
[[nodiscard]] int32_t get_user_latency() const { return compensator_.get_user_latency(); }
|
||||
[[nodiscard]] int32_t get_real_latency() const { return compensator_.get_real_latency(); }
|
||||
void set_user_latency(int32_t in_latency) { compensator_.set_latency(in_latency); }
|
||||
[[nodiscard]] circular_buffer_vector_type& get_ui_buffers() { return compensator_.ui_buffers; }
|
||||
|
||||
[[nodiscard]] std::string get_imgui_id() const {
|
||||
return get_name() + "##" + std::to_string(id_);
|
||||
@@ -68,12 +67,9 @@ public:
|
||||
|
||||
std::vector<plugin_host*> effects{};
|
||||
std::vector<mixer_track_link> children{};
|
||||
|
||||
// UI
|
||||
std::shared_ptr<circular_buffer_vector_type> ui_buffers;
|
||||
protected:
|
||||
virtual void on_latency_changed(int32_t in_latency);
|
||||
latency_compensator compensator_;
|
||||
latency_compensator compensator_;
|
||||
private:
|
||||
audio_buffer buffer_;
|
||||
audio_buffer temp_mix_buffer_;
|
||||
|
||||
Reference in New Issue
Block a user