docs: condense implementation comments
This commit is contained in:
@@ -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. */
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user