延迟补偿

This commit is contained in:
2024-07-04 20:18:35 +08:00
parent a161e5e2cd
commit 59c83ea212
6 changed files with 151 additions and 147 deletions

View File

@@ -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_;
}
};

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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_;