From dcddb94125512fbd35fe6f7545db3a805c794efa Mon Sep 17 00:00:00 2001 From: Neko-Life Date: Mon, 24 Aug 2026 14:34:08 +0700 Subject: [PATCH 1/8] fix: latency calculation --- include/dpp/discordvoiceclient.h | 5 +++ src/dpp/voice/enabled/constructor.cpp | 1 + src/dpp/voice/enabled/write_ready.cpp | 63 ++++++++++----------------- 3 files changed, 28 insertions(+), 41 deletions(-) diff --git a/include/dpp/discordvoiceclient.h b/include/dpp/discordvoiceclient.h index dcd048f16b..ac0e33ced7 100644 --- a/include/dpp/discordvoiceclient.h +++ b/include/dpp/discordvoiceclient.h @@ -560,6 +560,11 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ std::chrono::high_resolution_clock::time_point last_timestamp; + /** + * @brief Last sent packet duration + */ + uint64_t last_duration; + /** * @brief Fraction of the sleep that was not executed after the last audio packet was sent */ diff --git a/src/dpp/voice/enabled/constructor.cpp b/src/dpp/voice/enabled/constructor.cpp index ab2b3a9f59..c6bd52df34 100644 --- a/src/dpp/voice/enabled/constructor.cpp +++ b/src/dpp/voice/enabled/constructor.cpp @@ -50,6 +50,7 @@ discord_voice_client::discord_voice_client(dpp::cluster* _cluster, full_reconnec timestamp(0), packet_nonce(1), last_timestamp(std::chrono::high_resolution_clock::now()), + last_duration(0), sending(false), tracks(0), dave_version(enable_dave ? dave_version_1 : dave_version_none), diff --git a/src/dpp/voice/enabled/write_ready.cpp b/src/dpp/voice/enabled/write_ready.cpp index 4966ee2155..11df82123e 100644 --- a/src/dpp/voice/enabled/write_ready.cpp +++ b/src/dpp/voice/enabled/write_ready.cpp @@ -31,17 +31,23 @@ namespace dpp { void discord_voice_client::write_ready() { - bool needs_write = false; - { - std::lock_guard lock(this->stream_mutex); - const bool needs_stop_frames = this->paused && !this->sent_stop_frames; - const bool needs_send_audio = !this->paused && !outbuf.empty(); - needs_write = needs_stop_frames || needs_send_audio; - } + std::chrono::nanoseconds latency{0}; + if (send_audio_type != satype_live_audio) { + auto now = std::chrono::high_resolution_clock::now(); + + auto minimum = std::chrono::nanoseconds(last_duration); + auto elapsed = std::chrono::duration_cast(now - last_timestamp); - if (needs_write) { - udp_events.flags = WANT_READ | WANT_WRITE | WANT_ERROR; - owner->socketengine->update_socket(udp_events); + bool should_send_now = elapsed >= minimum; + if (!should_send_now) { + std::this_thread::sleep_for(minimum - elapsed); + udp_events.flags = WANT_READ | WANT_WRITE | WANT_ERROR; + owner->socketengine->update_socket(udp_events); + return; + } + + latency = elapsed - minimum; + last_timestamp = now; } uint64_t duration = 0; @@ -74,40 +80,15 @@ void discord_voice_client::write_ready() { outbuf.erase(outbuf.begin()); } } + if (!outbuf.empty()) { + udp_events.flags = WANT_READ | WANT_WRITE | WANT_ERROR; + owner->socketengine->update_socket(udp_events); + } } } if (duration) { - if (type == satype_recorded_audio) { - std::chrono::nanoseconds latency = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - last_timestamp); - std::chrono::nanoseconds sleep_time = std::chrono::nanoseconds(duration) - latency; - if (sleep_time.count() > 0) { - std::this_thread::sleep_for(sleep_time); - } - } - else if (type == satype_overlap_audio) { - std::chrono::nanoseconds latency = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - last_timestamp); - std::chrono::nanoseconds sleep_time = std::chrono::nanoseconds(duration) + last_sleep_remainder - latency; - std::chrono::nanoseconds sleep_increment = (std::chrono::nanoseconds(duration) - latency) / AUDIO_OVERLAP_SLEEP_SAMPLES; - if (sleep_time.count() > 0) { - uint16_t samples_count = 0; - std::chrono::nanoseconds overshoot_accumulator{}; - - do { - std::chrono::high_resolution_clock::time_point start_sleep = std::chrono::high_resolution_clock::now(); - std::this_thread::sleep_for(sleep_increment); - std::chrono::high_resolution_clock::time_point end_sleep = std::chrono::high_resolution_clock::now(); - - samples_count++; - overshoot_accumulator += std::chrono::duration_cast(end_sleep - start_sleep) - sleep_increment; - sleep_time -= std::chrono::duration_cast(end_sleep - start_sleep); - } while (std::chrono::nanoseconds(overshoot_accumulator.count() / samples_count) + sleep_increment < sleep_time); - last_sleep_remainder = sleep_time; - } else { - last_sleep_remainder = std::chrono::nanoseconds(0); - } - } - - last_timestamp = std::chrono::high_resolution_clock::now(); + auto latcount = latency.count(); + last_duration = duration > latcount ? duration - latcount : duration; if (!creator->on_voice_buffer_send.empty()) { voice_buffer_send_t snd(owner, 0, ""); snd.buffer_size = bufsize; From 0b66d8f6ed94de494aeef67c5d95c5fcfb341639 Mon Sep 17 00:00:00 2001 From: Neko-Life Date: Mon, 24 Aug 2026 15:21:34 +0700 Subject: [PATCH 2/8] log latency for now --- src/dpp/voice/enabled/write_ready.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dpp/voice/enabled/write_ready.cpp b/src/dpp/voice/enabled/write_ready.cpp index 11df82123e..a258677747 100644 --- a/src/dpp/voice/enabled/write_ready.cpp +++ b/src/dpp/voice/enabled/write_ready.cpp @@ -43,11 +43,14 @@ void discord_voice_client::write_ready() { std::this_thread::sleep_for(minimum - elapsed); udp_events.flags = WANT_READ | WANT_WRITE | WANT_ERROR; owner->socketengine->update_socket(udp_events); + std::cout << "FALSE: elapsed(" << elapsed << ") < minimum(" << minimum << ")\n"; return; } latency = elapsed - minimum; last_timestamp = now; + + std::cout << "TRUE: elapsed(" << elapsed << ") >= minimum(" << minimum << ") latency(" << latency << ")\n"; } uint64_t duration = 0; From 95bf68aaf898a4b5d107b48b96fb6dff1ff6d170 Mon Sep 17 00:00:00 2001 From: Neko-Life Date: Tue, 25 Aug 2026 00:28:18 +0700 Subject: [PATCH 3/8] fix: remove log and unused variable --- src/dpp/voice/enabled/write_ready.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/dpp/voice/enabled/write_ready.cpp b/src/dpp/voice/enabled/write_ready.cpp index a258677747..7a14741bec 100644 --- a/src/dpp/voice/enabled/write_ready.cpp +++ b/src/dpp/voice/enabled/write_ready.cpp @@ -43,20 +43,16 @@ void discord_voice_client::write_ready() { std::this_thread::sleep_for(minimum - elapsed); udp_events.flags = WANT_READ | WANT_WRITE | WANT_ERROR; owner->socketengine->update_socket(udp_events); - std::cout << "FALSE: elapsed(" << elapsed << ") < minimum(" << minimum << ")\n"; return; } latency = elapsed - minimum; last_timestamp = now; - - std::cout << "TRUE: elapsed(" << elapsed << ") >= minimum(" << minimum << ") latency(" << latency << ")\n"; } uint64_t duration = 0; bool track_marker_found = false; uint64_t bufsize = 0; - send_audio_type_t type = satype_recorded_audio; { std::lock_guard lock(this->stream_mutex); if (this->paused) { @@ -67,7 +63,6 @@ void discord_voice_client::write_ready() { /* Fallthrough if paused */ } else if (!outbuf.empty()) { - type = send_audio_type; if (outbuf[0].packet.size() == sizeof(uint16_t) && (*(reinterpret_cast(outbuf[0].packet.data()))) == AUDIO_TRACK_MARKER) { outbuf.erase(outbuf.begin()); track_marker_found = true; From 341246393d66f431e3b03b6f25cfa1b1ec5dd238 Mon Sep 17 00:00:00 2001 From: Neko-Life Date: Tue, 25 Aug 2026 00:44:45 +0700 Subject: [PATCH 4/8] chore: update doc --- include/dpp/discordvoiceclient.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/dpp/discordvoiceclient.h b/include/dpp/discordvoiceclient.h index ac0e33ced7..a3d575063c 100644 --- a/include/dpp/discordvoiceclient.h +++ b/include/dpp/discordvoiceclient.h @@ -804,12 +804,11 @@ class DPP_EXPORT discord_voice_client : public websocket_client * audio data because Discord does not expect to receive, say, 3 minutes' * worth of audio data in 1 second. * - * There are some inaccuracies in the throttling method used by the recorded + * There was some inaccuracies in the throttling method used by the recorded * audio mode on some systems (mainly Windows) which causes gaps and stutters * in the resulting audio stream. The overlap audio mode provides a different - * implementation that fixes the issue. This method is slightly more CPU - * intensive, and should only be used if you encounter issues with recorded audio - * on your system. + * implementation that fixes the issue in the past. This method is not used + * anymore and behave the same as the recorded audio mode. * * Use discord_voice_client::set_send_audio_type to change this value as * it ensures thread safety. From 9a453804085bb9db60d4345f7872ac7e8980ff77 Mon Sep 17 00:00:00 2001 From: Neko-Life Date: Tue, 25 Aug 2026 05:56:56 +0700 Subject: [PATCH 5/8] fix: make var const --- src/dpp/voice/enabled/write_ready.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/dpp/voice/enabled/write_ready.cpp b/src/dpp/voice/enabled/write_ready.cpp index 7a14741bec..c7cbba087b 100644 --- a/src/dpp/voice/enabled/write_ready.cpp +++ b/src/dpp/voice/enabled/write_ready.cpp @@ -33,13 +33,12 @@ namespace dpp { void discord_voice_client::write_ready() { std::chrono::nanoseconds latency{0}; if (send_audio_type != satype_live_audio) { - auto now = std::chrono::high_resolution_clock::now(); + const auto now = std::chrono::high_resolution_clock::now(); - auto minimum = std::chrono::nanoseconds(last_duration); - auto elapsed = std::chrono::duration_cast(now - last_timestamp); + const auto minimum = std::chrono::nanoseconds(last_duration); + const auto elapsed = std::chrono::duration_cast(now - last_timestamp); - bool should_send_now = elapsed >= minimum; - if (!should_send_now) { + if (elapsed >= minimum) { std::this_thread::sleep_for(minimum - elapsed); udp_events.flags = WANT_READ | WANT_WRITE | WANT_ERROR; owner->socketengine->update_socket(udp_events); From a3caae98fde09cfbea5edd6e80708eeee10102ce Mon Sep 17 00:00:00 2001 From: Neko-Life Date: Tue, 25 Aug 2026 05:58:20 +0700 Subject: [PATCH 6/8] fix: make var const --- src/dpp/voice/enabled/write_ready.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/voice/enabled/write_ready.cpp b/src/dpp/voice/enabled/write_ready.cpp index c7cbba087b..0a015661b6 100644 --- a/src/dpp/voice/enabled/write_ready.cpp +++ b/src/dpp/voice/enabled/write_ready.cpp @@ -38,7 +38,7 @@ void discord_voice_client::write_ready() { const auto minimum = std::chrono::nanoseconds(last_duration); const auto elapsed = std::chrono::duration_cast(now - last_timestamp); - if (elapsed >= minimum) { + if (elapsed < minimum) { std::this_thread::sleep_for(minimum - elapsed); udp_events.flags = WANT_READ | WANT_WRITE | WANT_ERROR; owner->socketengine->update_socket(udp_events); From 6496ee6d2e816003a202ffec53590086f9f92ae0 Mon Sep 17 00:00:00 2001 From: Neko-Life Date: Tue, 25 Aug 2026 06:08:11 +0700 Subject: [PATCH 7/8] fix: make var const --- src/dpp/voice/enabled/write_ready.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp/voice/enabled/write_ready.cpp b/src/dpp/voice/enabled/write_ready.cpp index 0a015661b6..53bf1cc6d3 100644 --- a/src/dpp/voice/enabled/write_ready.cpp +++ b/src/dpp/voice/enabled/write_ready.cpp @@ -84,7 +84,7 @@ void discord_voice_client::write_ready() { } } if (duration) { - auto latcount = latency.count(); + const auto latcount = latency.count(); last_duration = duration > latcount ? duration - latcount : duration; if (!creator->on_voice_buffer_send.empty()) { voice_buffer_send_t snd(owner, 0, ""); From 1347027e5e6d1ef3abba52e9cf1ff3f4fbebb181 Mon Sep 17 00:00:00 2001 From: Neko-Life Date: Tue, 25 Aug 2026 13:58:25 +0700 Subject: [PATCH 8/8] fix: remove unused field --- include/dpp/discordvoiceclient.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/dpp/discordvoiceclient.h b/include/dpp/discordvoiceclient.h index a3d575063c..ff32dc057e 100644 --- a/include/dpp/discordvoiceclient.h +++ b/include/dpp/discordvoiceclient.h @@ -565,11 +565,6 @@ class DPP_EXPORT discord_voice_client : public websocket_client */ uint64_t last_duration; - /** - * @brief Fraction of the sleep that was not executed after the last audio packet was sent - */ - std::chrono::nanoseconds last_sleep_remainder{}; - /** * @brief Maps receiving ssrc to user id */