docs: condense implementation comments
Some checks failed
Build Linux Binaries / linux/amd64 (push) Has been cancelled
Build Linux Binaries / linux/arm64 (push) Has been cancelled

This commit is contained in:
2026-07-23 13:37:05 +02:00
parent 575e2907d0
commit 4f71b784fe
22 changed files with 102 additions and 507 deletions

View File

@@ -132,7 +132,7 @@ typedef enum vc_event_type {
* vc_confirm_server_identity. Pins the TLS leaf certificate's own SHA-256 fingerprint
* (verifiable directly from the handshake), NOT the declared Ed25519
* server_identity_fingerprint from ServerHello — the TLS cert and the server's Ed25519
* identity key are generated independently with no cryptographic binding between them today
* identity key are generated independently with no cryptographic binding between them
* (docs/security.md §1.1), so pinning the self-declared value would be circular. The Ed25519
* fingerprint is still available for human-readable display via
* vc_get_server_identity_display(), it just isn't the value this gate accepts/rejects on. */

View File

@@ -632,9 +632,8 @@ void AudioEngine::on_playback(int16_t* out, ma_uint32 frames) {
// the engine-wide playback channel count
// dec_channels/frame_samples are bitstream properties (fixed at decoder init); `frames`
// below is the *hardware* playback callback's period, an independent value miniaudio
// picks on its own — opus_decode's max_samples must be frame_samples, never `frames`
// (see RemoteStream::ring in audio_engine.h for what went wrong when it was). The ring
// decouples the two: top it up by decoding whole Opus frames, then drain exactly
// picks on its own — opus_decode's max_samples must be frame_samples, never `frames`.
// The ring decouples the two: top it up by decoding whole Opus frames, then drain exactly
// `frames` samples-per-channel from it below (silence-padding on underrun = PLC).
const int dec_channels = std::max(1, stream.decoder.channels());
const int frame_samples = stream.decoder.frame_samples();

View File

@@ -1,7 +1,4 @@
/*
* audio/audio_engine.h: capture/playback + DSP + jitter buffer + mixer.
*
*/
/* Capture, playback, jitter buffering, and mixing. */
#ifndef VOICECAT_AUDIO_AUDIO_ENGINE_H
#define VOICECAT_AUDIO_AUDIO_ENGINE_H
@@ -394,13 +391,8 @@ class AudioEngine {
std::atomic<bool> running_{false};
std::atomic<float> output_volume_{1.0f};
// Capture-side frame accumulators: miniaudio fires the capture (and loopback) callback at
// whatever period the hardware/driver chooses — commonly 480 samples (10 ms) on WASAPI
// shared mode, while the Opus encoder requires exactly frame_samples_ per call (960 for
// 20 ms @ 48 kHz). Accumulate incoming PCM until a full frame is ready, then call
// capture_cb_. This mirrors the RemoteStream::ring fix on the playback side. Both
// accumulators are pre-allocated once in start(); never resized from the RT callback
// thread (satisfies architecture.md §3 — no allocation on RT threads).
// Device callback periods are independent of codec frame size. These preallocated
// accumulators emit complete frames without allocating on an RT thread.
struct CaptureAccum {
std::vector<int16_t> buf; // pre-sized to frame_samples_ in start()
int count = 0;
@@ -428,15 +420,10 @@ class AudioEngine {
float gain = 1.0f;
bool mute = false;
uint32_t playout_ts = 0;
// playout_ts free-runs (advances every callback via PLC), so it must be seeded from, and
// periodically re-synced to, the actual stream timeline — otherwise it drifts past the
// jitter buffer's drop window across VAD/PTT gaps and late joins and every frame is
// dropped/never-due (silent playback). false until the first frame seeds it (on_playback).
// Re-seeded from the stream timeline after late joins and transmission gaps.
bool playout_started = false;
// Set by push_recv_frame when a kFlagMarker (talkspurt-start) frame arrives; consumed by
// on_playback to force an immediate playout-clock reseed at the new talkspurt, so the
// bounded-depth target is re-established cleanly across silence gaps. See on_playback.
// A talkspurt marker forces playout-clock reseeding.
bool pending_marker = false;
// Diagnostic: times the decode/playback ring underran (produced silence because the
@@ -444,12 +431,7 @@ class AudioEngine {
// "frames arriving but silent / latency starved" signal. Polled via stream_underruns().
std::atomic<uint64_t> underruns{0};
// PLC cap (defense-in-depth): consecutive samples produced by packet-loss
// concealment since the last real decoded frame. Reset to 0 on every real frame.
// When it exceeds kPlcCapSamples (audio_engine.cpp), on_playback stops calling
// opus_decode(nullptr,0,...) and emits silence instead — bounding the comfort-noise
// hiss to ~2 s so a stale stream can never hiss forever even if remove_stream is
// never called. See on_playback's decode loop.
// Bounds consecutive PLC output so a stale stream eventually becomes silent.
int64_t plc_samples_since_real = 0;
// Listener-chosen, local-only noise reduction (docs/voice.md §10). Lazily
@@ -483,17 +465,8 @@ class AudioEngine {
std::atomic<int64_t> last_voice_ms{0};
bool talking = false;
// Decode/playback decoupling ring
// opus_decode() must be called with max_samples == the encoder's fixed frame size
// (decoder.frame_samples(), e.g. 960 @ 20ms/48kHz) — that's a property of the bitstream,
// not a choice. miniaudio's playback callback period is a *separate*, independently
// chosen value (often smaller, e.g. ~480 @ low-latency WASAPI defaults) and must never
// be passed to opus_decode as max_samples (doing so made decode fail basically every
// callback — silent playback bug, fixed by this ring). on_playback() tops this ring up
// by decoding whole Opus frames (decoder's channel count) and drains exactly the
// hardware-requested sample count from it each callback, padding with silence (PLC) on
// underrun. Sized once in init_ring() (called off the audio thread); never resized from
// on_playback (real-time rule).
// Decoding uses the bitstream frame size, while playback drains the device callback
// size. This preallocated ring decouples those clocks and is never resized on the RT path.
std::vector<int16_t> ring; // capacity = (frame_samples * 8) frames * ring_channels
size_t ring_channels = 1;
size_t ring_head = 0; // next frame (sample-per-channel) to read

View File

@@ -950,9 +950,7 @@ int64_t client_now_ms() {
.count();
}
// Builds an OpusParams from a wire AudioConfig, applying the same field-by-field mapping on
// both the send (local-stream encoder) and receive (remote-stream decoder) paths — fixes a
// gap where mode/dtx/complexity/application were silently dropped.
// Maps the complete wire AudioConfig for both local encoders and remote decoders.
voicecat::codec::OpusParams opus_params_from_audio_config(const voicecat::v1::AudioConfig& a) {
voicecat::codec::OpusParams p;
// Opus always runs at 48 kHz internally: the whole AudioEngine clock is

View File

@@ -333,9 +333,8 @@ void TcpServerConn::wait_closed() {
namespace {
// Try IPv6 dual-stack first (one socket handles both ::1 and 127.0.0.1 — fixes the common
// Windows case where `localhost` resolves to ::1 before 127.0.0.1). Falls back to IPv4-only
// if the OS has IPv6 disabled or the dual-stack bind fails for any reason.
// Prefer IPv6 dual-stack so one listener accepts both IPv6 and IPv4 localhost addresses.
// Fall back to IPv4 when dual-stack binding is unavailable.
asio::ip::tcp::acceptor make_acceptor(asio::io_context& io, uint16_t port) {
asio::ip::tcp::acceptor acc(io);
std::error_code ec;