chore: remove skeleton build mode and stub #ifdef scaffolding
Drop the M0 no-deps skeleton preset and all VOICECAT_HAS_NET/AUDIO/OPUS/NS guards that it required. Every subsystem is fully implemented; the stub #else paths were dead code that added noise to every header and source file. - CMakePresets.json: remove skeleton configure/build/test entries - CMakeLists.txt (root/core/tests): remove VOICECAT_USE_VCPKG_DEPS option and guards; all targets now build unconditionally - 17 C++ source files: unwrap HAS_* guards, delete stub #else blocks - apm_processor.cpp: delete ApmPassthrough no-op class; create() always returns RnnoiseProcessor - 18 test files: remove HAS_* guards and stub int main() skip bodies - docs/building.md: remove skeleton from preset table and prose VOICECAT_HAS_LOOPBACK (Windows WASAPI loopback platform gate) unchanged. 29/29 ctest green. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,11 +14,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ── Options ───────────────────────────────────────────────────────────────────
|
# ── Options ───────────────────────────────────────────────────────────────────
|
||||||
# The M0 skeleton compiles with NO third-party dependencies: every subsystem is a
|
|
||||||
# stub that returns VC_ERR_NOT_IMPLEMENTED. As each subsystem is built out, flip
|
|
||||||
# VOICECAT_USE_VCPKG_DEPS=ON so CMake pulls the real libraries (mbedTLS, libsodium,
|
|
||||||
# opus, protobuf, ...) via the vcpkg toolchain (see vcpkg.json / docs/tech-stack.md).
|
|
||||||
option(VOICECAT_USE_VCPKG_DEPS "Link real third-party deps via vcpkg" OFF)
|
|
||||||
option(VOICECAT_BUILD_SERVER "Build voicecat-server" ON)
|
option(VOICECAT_BUILD_SERVER "Build voicecat-server" ON)
|
||||||
option(VOICECAT_BUILD_TOOLS "Build the vccli headless test client" ON)
|
option(VOICECAT_BUILD_TOOLS "Build the vccli headless test client" ON)
|
||||||
option(VOICECAT_BUILD_TESTS "Build tests" ON)
|
option(VOICECAT_BUILD_TESTS "Build tests" ON)
|
||||||
@@ -60,9 +55,7 @@ endif()
|
|||||||
|
|
||||||
if(VOICECAT_BUILD_TOOLS)
|
if(VOICECAT_BUILD_TOOLS)
|
||||||
add_subdirectory(tools/vccli)
|
add_subdirectory(tools/vccli)
|
||||||
if(VOICECAT_USE_VCPKG_DEPS)
|
add_subdirectory(tools/voicecat-admin)
|
||||||
add_subdirectory(tools/voicecat-admin)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(VOICECAT_BUILD_TESTS)
|
if(VOICECAT_BUILD_TESTS)
|
||||||
@@ -71,5 +64,4 @@ if(VOICECAT_BUILD_TESTS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "VoiceCat ${PROJECT_VERSION} configured "
|
message(STATUS "VoiceCat ${PROJECT_VERSION} configured "
|
||||||
"(vcpkg deps: ${VOICECAT_USE_VCPKG_DEPS}, "
|
"(server: ${VOICECAT_BUILD_SERVER}, tools: ${VOICECAT_BUILD_TOOLS})")
|
||||||
"server: ${VOICECAT_BUILD_SERVER}, tools: ${VOICECAT_BUILD_TOOLS})")
|
|
||||||
|
|||||||
@@ -12,17 +12,6 @@
|
|||||||
"VOICECAT_USE_VCPKG_DEPS": "ON"
|
"VOICECAT_USE_VCPKG_DEPS": "ON"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "skeleton",
|
|
||||||
"displayName": "Skeleton (no third-party deps)",
|
|
||||||
"description": "Builds the stub skeleton with just a C++20 compiler — no vcpkg needed. Subsystems return VC_ERR_NOT_IMPLEMENTED. Good for 'does the repo even build' smoke checks. Runs 2 tests (smoke + frame_codec).",
|
|
||||||
"generator": "Ninja",
|
|
||||||
"binaryDir": "${sourceDir}/build/skeleton",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_BUILD_TYPE": "Debug",
|
|
||||||
"VOICECAT_USE_VCPKG_DEPS": "OFF"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "dev",
|
"name": "dev",
|
||||||
"displayName": "Dev (full real-deps build, vcpkg)",
|
"displayName": "Dev (full real-deps build, vcpkg)",
|
||||||
@@ -130,7 +119,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"buildPresets": [
|
"buildPresets": [
|
||||||
{ "name": "skeleton", "configurePreset": "skeleton" },
|
|
||||||
{ "name": "dev", "configurePreset": "dev" },
|
{ "name": "dev", "configurePreset": "dev" },
|
||||||
{ "name": "release", "configurePreset": "release" },
|
{ "name": "release", "configurePreset": "release" },
|
||||||
{ "name": "server-release", "configurePreset": "server-release" },
|
{ "name": "server-release", "configurePreset": "server-release" },
|
||||||
@@ -140,7 +128,6 @@
|
|||||||
{ "name": "apple-ios-sim", "configurePreset": "apple-ios-sim" }
|
{ "name": "apple-ios-sim", "configurePreset": "apple-ios-sim" }
|
||||||
],
|
],
|
||||||
"testPresets": [
|
"testPresets": [
|
||||||
{ "name": "skeleton", "configurePreset": "skeleton", "output": { "outputOnFailure": true } },
|
|
||||||
{ "name": "dev", "configurePreset": "dev", "output": { "outputOnFailure": true } },
|
{ "name": "dev", "configurePreset": "dev", "output": { "outputOnFailure": true } },
|
||||||
{ "name": "release", "configurePreset": "release", "output": { "outputOnFailure": true } }
|
{ "name": "release", "configurePreset": "release", "output": { "outputOnFailure": true } }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ set_target_properties(voicecat PROPERTIES
|
|||||||
CXX_VISIBILITY_PRESET hidden
|
CXX_VISIBILITY_PRESET hidden
|
||||||
VISIBILITY_INLINES_HIDDEN ON)
|
VISIBILITY_INLINES_HIDDEN ON)
|
||||||
|
|
||||||
if(VOICECAT_USE_VCPKG_DEPS)
|
find_package(protobuf CONFIG REQUIRED)
|
||||||
find_package(protobuf CONFIG REQUIRED)
|
|
||||||
find_package(unofficial-sodium CONFIG REQUIRED)
|
find_package(unofficial-sodium CONFIG REQUIRED)
|
||||||
find_package(MbedTLS CONFIG REQUIRED)
|
find_package(MbedTLS CONFIG REQUIRED)
|
||||||
find_package(asio CONFIG REQUIRED)
|
find_package(asio CONFIG REQUIRED)
|
||||||
@@ -129,4 +128,3 @@ if(VOICECAT_USE_VCPKG_DEPS)
|
|||||||
|
|
||||||
# Signal to C++ code that the real networking/crypto stack is available.
|
# Signal to C++ code that the real networking/crypto stack is available.
|
||||||
target_compile_definitions(voicecat PUBLIC VOICECAT_HAS_NET)
|
target_compile_definitions(voicecat PUBLIC VOICECAT_HAS_NET)
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -8,13 +8,9 @@
|
|||||||
* Design: docs/architecture.md §4. Everything here is async + event-driven — calls return
|
* Design: docs/architecture.md §4. Everything here is async + event-driven — calls return
|
||||||
* immediately and results/state changes arrive via the vc_callbacks.on_event callback.
|
* immediately and results/state changes arrive via the vc_callbacks.on_event callback.
|
||||||
*
|
*
|
||||||
* STATUS: real, behind VOICECAT_HAS_NET (the `dev`/`release`/`server-release` presets —
|
* STATUS: real. Control plane, voice, multi-stream, device enumeration, VAD/PTT, and stereo
|
||||||
* vcpkg deps on; see docs/building.md). As of M3, control plane, voice, multi-stream, device
|
* playback all work via core/src/core/client.cpp. webrtc AEC/NS/AGC remains an inert passthrough
|
||||||
* enumeration, VAD/PTT, and stereo playback all work for real via core/src/core/client.cpp.
|
* (no Windows/MSVC port upstream — docs/voice.md §8/§11, PROGRESS.md).
|
||||||
* The no-deps `skeleton` preset still links a stub vc_client that returns VC_ERR_NOT_IMPLEMENTED
|
|
||||||
* for everything below `connect`, purely to keep that skeleton build green. webrtc AEC/NS/AGC
|
|
||||||
* remains an inert passthrough regardless of preset (no Windows/MSVC port upstream —
|
|
||||||
* docs/voice.md §8/§11, PROGRESS.md).
|
|
||||||
*/
|
*/
|
||||||
#ifndef VOICECAT_H
|
#ifndef VOICECAT_H
|
||||||
#define VOICECAT_H
|
#define VOICECAT_H
|
||||||
@@ -56,7 +52,7 @@ extern "C" {
|
|||||||
/* ── Result codes ─────────────────────────────────────────────────────────── */
|
/* ── Result codes ─────────────────────────────────────────────────────────── */
|
||||||
typedef enum vc_result {
|
typedef enum vc_result {
|
||||||
VC_OK = 0,
|
VC_OK = 0,
|
||||||
VC_ERR_NOT_IMPLEMENTED = 1, /* skeleton stub */
|
VC_ERR_NOT_IMPLEMENTED = 1,
|
||||||
VC_ERR_INVALID_ARG = 2,
|
VC_ERR_INVALID_ARG = 2,
|
||||||
VC_ERR_NOT_CONNECTED = 3,
|
VC_ERR_NOT_CONNECTED = 3,
|
||||||
VC_ERR_ALREADY = 4,
|
VC_ERR_ALREADY = 4,
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NS
|
|
||||||
#include "rnnoise.h"
|
#include "rnnoise.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace voicecat::audio {
|
namespace voicecat::audio {
|
||||||
|
|
||||||
@@ -19,15 +17,6 @@ int64_t steady_now_ms() {
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// ── ApmPassthrough ────────────────────────────────────────────────────────────
|
|
||||||
// No-op: returns true (VAD always open), does not modify PCM.
|
|
||||||
// Replaced by WebrtcApmProcessor when VOICECAT_HAS_APM is defined.
|
|
||||||
class ApmPassthrough final : public ApmProcessor {
|
|
||||||
public:
|
|
||||||
void process_render(const int16_t*, int, int) override {}
|
|
||||||
bool process_capture(int16_t*, int, int) override { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── EnergyVadProcessor ──────────────────────────────────────────────────────
|
// ── EnergyVadProcessor ──────────────────────────────────────────────────────
|
||||||
// Lightweight, dependency-free energy/RMS VAD — see apm_processor.h's create_vad() doc comment
|
// Lightweight, dependency-free energy/RMS VAD — see apm_processor.h's create_vad() doc comment
|
||||||
// for why this exists instead of a real APM. No AEC (process_render is a no-op); doesn't modify
|
// for why this exists instead of a real APM. No AEC (process_render is a no-op); doesn't modify
|
||||||
@@ -62,7 +51,6 @@ class EnergyVadProcessor final : public ApmProcessor {
|
|||||||
int64_t last_voice_ms_ = 0; // epoch start -> gate begins closed until first loud frame
|
int64_t last_voice_ms_ = 0; // epoch start -> gate begins closed until first loud frame
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NS
|
|
||||||
// ── RnnoiseProcessor ─────────────────────────────────────────────────────────
|
// ── RnnoiseProcessor ─────────────────────────────────────────────────────────
|
||||||
// Real noise suppression via vendored RNNoise (third_party/rnnoise; docs/voice.md §10-11).
|
// Real noise suppression via vendored RNNoise (third_party/rnnoise; docs/voice.md §10-11).
|
||||||
// RNNoise is a mono, 48 kHz, fixed 480-sample (10 ms) speech denoiser; our engine clock is fixed
|
// RNNoise is a mono, 48 kHz, fixed 480-sample (10 ms) speech denoiser; our engine clock is fixed
|
||||||
@@ -103,14 +91,9 @@ class RnnoiseProcessor final : public ApmProcessor {
|
|||||||
float in_[kFrame];
|
float in_[kFrame];
|
||||||
float out_[kFrame];
|
float out_[kFrame];
|
||||||
};
|
};
|
||||||
#endif // VOICECAT_HAS_NS
|
|
||||||
|
|
||||||
std::unique_ptr<ApmProcessor> ApmProcessor::create() {
|
std::unique_ptr<ApmProcessor> ApmProcessor::create() {
|
||||||
#ifdef VOICECAT_HAS_NS
|
|
||||||
return std::make_unique<RnnoiseProcessor>();
|
return std::make_unique<RnnoiseProcessor>();
|
||||||
#else
|
|
||||||
return std::make_unique<ApmPassthrough>();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ApmProcessor> ApmProcessor::create_vad(float rms_threshold,
|
std::unique_ptr<ApmProcessor> ApmProcessor::create_vad(float rms_threshold,
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
#define MINIAUDIO_IMPLEMENTATION
|
#define MINIAUDIO_IMPLEMENTATION
|
||||||
#include <miniaudio.h>
|
#include <miniaudio.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "audio/audio_engine.h"
|
#include "audio/audio_engine.h"
|
||||||
|
|
||||||
@@ -41,7 +39,6 @@ constexpr int32_t kStarveSamples = 48000 * 120 / 1000; // reseed when clock
|
|||||||
// defense-in-depth against any future regression that skips remove_stream.
|
// defense-in-depth against any future regression that skips remove_stream.
|
||||||
constexpr int32_t kPlcCapSamples = 48000 * 2; // 2 s @ 48 kHz
|
constexpr int32_t kPlcCapSamples = 48000 * 2; // 2 s @ 48 kHz
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
// device_id encoding (DeviceInfo::id / AudioParams::*_device_id): a hex string of the raw
|
// device_id encoding (DeviceInfo::id / AudioParams::*_device_id): a hex string of the raw
|
||||||
// ma_device_id bytes. Opaque on purpose — names aren't guaranteed unique, and this is the only
|
// ma_device_id bytes. Opaque on purpose — names aren't guaranteed unique, and this is the only
|
||||||
// stable handle miniaudio accepts back for device selection. Internal contract only; never
|
// stable handle miniaudio accepts back for device selection. Internal contract only; never
|
||||||
@@ -76,7 +73,6 @@ bool hex_decode_device_id(const std::string& hex, ma_device_id* out) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif // VOICECAT_HAS_AUDIO
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// ── JitterBuffer ─────────────────────────────────────────────────────────────
|
// ── JitterBuffer ─────────────────────────────────────────────────────────────
|
||||||
@@ -197,19 +193,14 @@ AudioEngine::AudioEngine() = default;
|
|||||||
|
|
||||||
AudioEngine::~AudioEngine() {
|
AudioEngine::~AudioEngine() {
|
||||||
stop();
|
stop();
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
stop_loopback_capture(); // loopback has an independent lifecycle — close it before the context
|
stop_loopback_capture(); // loopback has an independent lifecycle — close it before the context
|
||||||
if (context_inited_) {
|
if (context_inited_) {
|
||||||
ma_context_uninit(&context_);
|
ma_context_uninit(&context_);
|
||||||
context_inited_ = false;
|
context_inited_ = false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
if (dred_dec_) { opus_dred_decoder_destroy(dred_dec_); dred_dec_ = nullptr; }
|
if (dred_dec_) { opus_dred_decoder_destroy(dred_dec_); dred_dec_ = nullptr; }
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
ma_context_config AudioEngine::make_context_config() {
|
ma_context_config AudioEngine::make_context_config() {
|
||||||
ma_context_config cfg = ma_context_config_init();
|
ma_context_config cfg = ma_context_config_init();
|
||||||
// iOS: leave AVAudioSession entirely to the Swift layer (IOSAudioRouter). See the
|
// iOS: leave AVAudioSession entirely to the Swift layer (IOSAudioRouter). See the
|
||||||
@@ -219,7 +210,6 @@ ma_context_config AudioEngine::make_context_config() {
|
|||||||
cfg.coreaudio.noAudioSessionDeactivate = MA_TRUE; // don't setActive(false) on device uninit
|
cfg.coreaudio.noAudioSessionDeactivate = MA_TRUE; // don't setActive(false) on device uninit
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
bool AudioEngine::start(const AudioParams& p, CaptureCallback capture_cb) {
|
bool AudioEngine::start(const AudioParams& p, CaptureCallback capture_cb) {
|
||||||
if (running_.load()) return false;
|
if (running_.load()) return false;
|
||||||
@@ -227,14 +217,11 @@ bool AudioEngine::start(const AudioParams& p, CaptureCallback capture_cb) {
|
|||||||
capture_cb_ = std::move(capture_cb);
|
capture_cb_ = std::move(capture_cb);
|
||||||
frame_samples_ = static_cast<int>(p.sample_rate / 1000 * p.frame_ms);
|
frame_samples_ = static_cast<int>(p.sample_rate / 1000 * p.frame_ms);
|
||||||
running_.store(true, std::memory_order_release);
|
running_.store(true, std::memory_order_release);
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
if (!dred_dec_) {
|
if (!dred_dec_) {
|
||||||
int err = 0;
|
int err = 0;
|
||||||
dred_dec_ = opus_dred_decoder_create(&err); // null on failure — DRED silently disabled
|
dred_dec_ = opus_dred_decoder_create(&err); // null on failure — DRED silently disabled
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
// Pre-allocate capture accumulators before the devices start so on_capture / on_loopback
|
// Pre-allocate capture accumulators before the devices start so on_capture / on_loopback
|
||||||
// never allocate on the RT thread. count=0 means "empty"; the buf is sized to exactly one
|
// never allocate on the RT thread. count=0 means "empty"; the buf is sized to exactly one
|
||||||
// encoder frame so a memcpy into it can never overrun. The mic accumulator is sized to
|
// encoder frame so a memcpy into it can never overrun. The mic accumulator is sized to
|
||||||
@@ -247,9 +234,7 @@ bool AudioEngine::start(const AudioParams& p, CaptureCallback capture_cb) {
|
|||||||
capture_accum_.count = 0;
|
capture_accum_.count = 0;
|
||||||
loopback_accum_.buf.assign(static_cast<size_t>(frame_samples_), 0);
|
loopback_accum_.buf.assign(static_cast<size_t>(frame_samples_), 0);
|
||||||
loopback_accum_.count = 0;
|
loopback_accum_.count = 0;
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
// Own the ma_context (lazily, reused across restarts) so miniaudio does not touch
|
// Own the ma_context (lazily, reused across restarts) so miniaudio does not touch
|
||||||
// AVAudioSession on iOS — the Swift IOSAudioRouter is the sole session owner. Without
|
// AVAudioSession on iOS — the Swift IOSAudioRouter is the sole session owner. Without
|
||||||
// this, ma_device_init(nullptr, ...) below would reset the session category to Record/
|
// this, ma_device_init(nullptr, ...) below would reset the session category to Record/
|
||||||
@@ -342,14 +327,12 @@ bool AudioEngine::start(const AudioParams& p, CaptureCallback capture_cb) {
|
|||||||
mixer_timer_stop_.store(false, std::memory_order_release);
|
mixer_timer_stop_.store(false, std::memory_order_release);
|
||||||
mixer_timer_thread_ = std::thread([this] { run_mixer_timer(); });
|
mixer_timer_thread_ = std::thread([this] { run_mixer_timer(); });
|
||||||
}
|
}
|
||||||
#endif // VOICECAT_HAS_AUDIO
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<DeviceInfo> AudioEngine::enumerate_devices(bool capture) {
|
std::vector<DeviceInfo> AudioEngine::enumerate_devices(bool capture) {
|
||||||
std::vector<DeviceInfo> result;
|
std::vector<DeviceInfo> result;
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
// Use the no-AVAudioSession-management config here too: device enumeration runs at
|
// Use the no-AVAudioSession-management config here too: device enumeration runs at
|
||||||
// SessionState init (refreshDevices) and on settings views, and a default-config context
|
// SessionState init (refreshDevices) and on settings views, and a default-config context
|
||||||
// would call setCategory()/setActive() on iOS, disrupting the session the Swift layer owns.
|
// would call setCategory()/setActive() on iOS, disrupting the session the Swift layer owns.
|
||||||
@@ -377,16 +360,12 @@ std::vector<DeviceInfo> AudioEngine::enumerate_devices(bool capture) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ma_context_uninit(&ctx);
|
ma_context_uninit(&ctx);
|
||||||
#else
|
|
||||||
(void)capture;
|
|
||||||
#endif // VOICECAT_HAS_AUDIO
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEngine::stop() {
|
void AudioEngine::stop() {
|
||||||
if (!running_.exchange(false)) return;
|
if (!running_.exchange(false)) return;
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
// External playback: stop + join the mixer-timer thread before tearing down state it reads.
|
// External playback: stop + join the mixer-timer thread before tearing down state it reads.
|
||||||
mixer_timer_stop_.store(true, std::memory_order_release);
|
mixer_timer_stop_.store(true, std::memory_order_release);
|
||||||
if (mixer_timer_thread_.joinable()) mixer_timer_thread_.join();
|
if (mixer_timer_thread_.joinable()) mixer_timer_thread_.join();
|
||||||
@@ -400,11 +379,9 @@ void AudioEngine::stop() {
|
|||||||
ma_device_uninit(&playback_device_);
|
ma_device_uninit(&playback_device_);
|
||||||
playback_started_ = false;
|
playback_started_ = false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioEngine::suspend() {
|
bool AudioEngine::suspend() {
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
if (!running_.load(std::memory_order_acquire)) return true;
|
if (!running_.load(std::memory_order_acquire)) return true;
|
||||||
// External playback: pause the mixer timer (there is no playback device to stop). This
|
// External playback: pause the mixer timer (there is no playback device to stop). This
|
||||||
// matches the VPIO renderer going down during an AVAudioSession interruption.
|
// matches the VPIO renderer going down during an AVAudioSession interruption.
|
||||||
@@ -416,13 +393,9 @@ bool AudioEngine::suspend() {
|
|||||||
if (capture_started_) ok &= (ma_device_stop(&capture_device_) == MA_SUCCESS);
|
if (capture_started_) ok &= (ma_device_stop(&capture_device_) == MA_SUCCESS);
|
||||||
if (playback_started_) ok &= (ma_device_stop(&playback_device_) == MA_SUCCESS);
|
if (playback_started_) ok &= (ma_device_stop(&playback_device_) == MA_SUCCESS);
|
||||||
return ok;
|
return ok;
|
||||||
#else
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioEngine::resume() {
|
bool AudioEngine::resume() {
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
if (!running_.load(std::memory_order_acquire)) return true;
|
if (!running_.load(std::memory_order_acquire)) return true;
|
||||||
// External playback: relaunch the mixer timer (mixer_scratch_ is still sized from start()).
|
// External playback: relaunch the mixer timer (mixer_scratch_ is still sized from start()).
|
||||||
if (external_playback_ && !mixer_timer_thread_.joinable()) {
|
if (external_playback_ && !mixer_timer_thread_.joinable()) {
|
||||||
@@ -433,9 +406,6 @@ bool AudioEngine::resume() {
|
|||||||
if (capture_started_) ok &= (ma_device_start(&capture_device_) == MA_SUCCESS);
|
if (capture_started_) ok &= (ma_device_start(&capture_device_) == MA_SUCCESS);
|
||||||
if (playback_started_) ok &= (ma_device_start(&playback_device_) == MA_SUCCESS);
|
if (playback_started_) ok &= (ma_device_start(&playback_device_) == MA_SUCCESS);
|
||||||
return ok;
|
return ok;
|
||||||
#else
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEngine::inject_capture(int kind, const int16_t* pcm, size_t samples_per_channel,
|
void AudioEngine::inject_capture(int kind, const int16_t* pcm, size_t samples_per_channel,
|
||||||
@@ -530,12 +500,10 @@ void AudioEngine::remove_stream(uint32_t ssrc) {
|
|||||||
std::lock_guard lk(streams_mu_);
|
std::lock_guard lk(streams_mu_);
|
||||||
auto it = streams_.find(ssrc);
|
auto it = streams_.find(ssrc);
|
||||||
if (it != streams_.end()) {
|
if (it != streams_.end()) {
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
if (it->second.dred_state_) {
|
if (it->second.dred_state_) {
|
||||||
opus_dred_free(it->second.dred_state_);
|
opus_dred_free(it->second.dred_state_);
|
||||||
it->second.dred_state_ = nullptr;
|
it->second.dred_state_ = nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
streams_.erase(it);
|
streams_.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -590,7 +558,6 @@ int32_t AudioEngine::stream_playout_depth_samples(uint32_t ssrc) const {
|
|||||||
return static_cast<int32_t>(*newest - it->second.playout_ts);
|
return static_cast<int32_t>(*newest - it->second.playout_ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
void AudioEngine::init_recv_stream(uint32_t ssrc, const codec::OpusParams& p,
|
void AudioEngine::init_recv_stream(uint32_t ssrc, const codec::OpusParams& p,
|
||||||
uint32_t user_id, uint32_t stream_id, bool is_voice) {
|
uint32_t user_id, uint32_t stream_id, bool is_voice) {
|
||||||
std::lock_guard lk(streams_mu_);
|
std::lock_guard lk(streams_mu_);
|
||||||
@@ -616,7 +583,6 @@ void AudioEngine::init_recv_stream(uint32_t ssrc, const codec::OpusParams& p,
|
|||||||
stream.dred_state_ = opus_dred_alloc(&err); // null on failure — falls back to PLC
|
stream.dred_state_ = opus_dred_alloc(&err); // null on failure — falls back to PLC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void AudioEngine::set_pcm_sink(PcmSink cb, void* user) {
|
void AudioEngine::set_pcm_sink(PcmSink cb, void* user) {
|
||||||
pcm_sink_user_.store(user, std::memory_order_relaxed);
|
pcm_sink_user_.store(user, std::memory_order_relaxed);
|
||||||
@@ -628,8 +594,6 @@ void AudioEngine::set_mixed_output_sink(MixedSink cb, void* user) {
|
|||||||
mixed_sink_.store(cb, std::memory_order_release);
|
mixed_sink_.store(cb, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
|
|
||||||
void AudioEngine::capture_data_cb(ma_device* dev, void* /*out*/,
|
void AudioEngine::capture_data_cb(ma_device* dev, void* /*out*/,
|
||||||
const void* in, ma_uint32 frame_count) {
|
const void* in, ma_uint32 frame_count) {
|
||||||
auto* self = static_cast<AudioEngine*>(dev->pUserData);
|
auto* self = static_cast<AudioEngine*>(dev->pUserData);
|
||||||
@@ -678,7 +642,6 @@ void AudioEngine::on_playback(int16_t* out, ma_uint32 frames) {
|
|||||||
std::unique_lock lk(streams_mu_, std::try_to_lock);
|
std::unique_lock lk(streams_mu_, std::try_to_lock);
|
||||||
if (!lk) return; // contended: emit silence this period
|
if (!lk) return; // contended: emit silence this period
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
std::vector<int32_t> mix(frames * pb_channels, 0);
|
std::vector<int32_t> mix(frames * pb_channels, 0);
|
||||||
|
|
||||||
for (auto& [ssrc, stream] : streams_) {
|
for (auto& [ssrc, stream] : streams_) {
|
||||||
@@ -747,7 +710,6 @@ void AudioEngine::on_playback(int16_t* out, ma_uint32 frames) {
|
|||||||
// embeds in the next packet) → PLC comfort noise. DRED and FEC both need the
|
// embeds in the next packet) → PLC comfort noise. DRED and FEC both need the
|
||||||
// *next* packet already buffered, so copy it once and try each in turn.
|
// *next* packet already buffered, so copy it once and try each in turn.
|
||||||
n = -1;
|
n = -1;
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
uint32_t next_ts = stream.playout_ts + static_cast<uint32_t>(frame_samples);
|
uint32_t next_ts = stream.playout_ts + static_cast<uint32_t>(frame_samples);
|
||||||
size_t psz = stream.jitter.try_copy_front_payload(
|
size_t psz = stream.jitter.try_copy_front_payload(
|
||||||
next_ts, stream.dred_payload_scratch_.data(),
|
next_ts, stream.dred_payload_scratch_.data(),
|
||||||
@@ -777,7 +739,6 @@ void AudioEngine::on_playback(int16_t* out, ma_uint32 frames) {
|
|||||||
stream.dred_payload_scratch_.data(), static_cast<int>(psz),
|
stream.dred_payload_scratch_.data(), static_cast<int>(psz),
|
||||||
stream.decode_scratch.data(), frame_samples, /*fec=*/true);
|
stream.decode_scratch.data(), frame_samples, /*fec=*/true);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
// 3. PLC: synthesize a continuation when no redundancy is available.
|
// 3. PLC: synthesize a continuation when no redundancy is available.
|
||||||
if (n <= 0) {
|
if (n <= 0) {
|
||||||
n = stream.decoder.decode(nullptr, 0, stream.decode_scratch.data(),
|
n = stream.decoder.decode(nullptr, 0, stream.decode_scratch.data(),
|
||||||
@@ -858,10 +819,6 @@ void AudioEngine::on_playback(int16_t* out, ma_uint32 frames) {
|
|||||||
int32_t s = static_cast<int32_t>(static_cast<float>(mix[i]) * ovol);
|
int32_t s = static_cast<int32_t>(static_cast<float>(mix[i]) * ovol);
|
||||||
out[i] = static_cast<int16_t>(std::clamp(s, -32768, 32767));
|
out[i] = static_cast<int16_t>(std::clamp(s, -32768, 32767));
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
(void)out;
|
|
||||||
(void)frames;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// External-playback timer (iOS VPIO): with no hardware playback device to "pull" frames, this
|
// External-playback timer (iOS VPIO): with no hardware playback device to "pull" frames, this
|
||||||
@@ -986,11 +943,4 @@ bool AudioEngine::start_loopback_capture(int /*kind*/, int /*channels*/) { retur
|
|||||||
void AudioEngine::stop_loopback_capture() {}
|
void AudioEngine::stop_loopback_capture() {}
|
||||||
#endif // VOICECAT_HAS_LOOPBACK
|
#endif // VOICECAT_HAS_LOOPBACK
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_AUDIO
|
|
||||||
|
|
||||||
#ifndef VOICECAT_HAS_AUDIO
|
|
||||||
bool AudioEngine::start_loopback_capture(int /*kind*/, int /*channels*/) { return false; }
|
|
||||||
void AudioEngine::stop_loopback_capture() {}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace voicecat::audio
|
} // namespace voicecat::audio
|
||||||
|
|||||||
@@ -25,14 +25,10 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
// miniaudio single-header — MINIAUDIO_IMPLEMENTATION defined in audio_engine.cpp
|
// miniaudio single-header — MINIAUDIO_IMPLEMENTATION defined in audio_engine.cpp
|
||||||
#include <miniaudio.h>
|
#include <miniaudio.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
#include "codec/opus_codec.h"
|
#include "codec/opus_codec.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "audio/apm_processor.h"
|
#include "audio/apm_processor.h"
|
||||||
|
|
||||||
@@ -222,7 +218,6 @@ class AudioEngine {
|
|||||||
// bounded-depth invariant. Returns 0 if the stream is unknown or not yet playing out.
|
// bounded-depth invariant. Returns 0 if the stream is unknown or not yet playing out.
|
||||||
int32_t stream_playout_depth_samples(uint32_t ssrc) const;
|
int32_t stream_playout_depth_samples(uint32_t ssrc) const;
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
// Configure the Opus decoder for an incoming ssrc (must be called before
|
// Configure the Opus decoder for an incoming ssrc (must be called before
|
||||||
// push_recv_frame for that ssrc). user_id/stream_id identify the source for the
|
// push_recv_frame for that ssrc). user_id/stream_id identify the source for the
|
||||||
// pcm_sink_ callback. is_voice marks a MIC stream so the receive-side NR pass knows it may
|
// pcm_sink_ callback. is_voice marks a MIC stream so the receive-side NR pass knows it may
|
||||||
@@ -230,7 +225,6 @@ class AudioEngine {
|
|||||||
// Thread-safe.
|
// Thread-safe.
|
||||||
void init_recv_stream(uint32_t ssrc, const codec::OpusParams& p,
|
void init_recv_stream(uint32_t ssrc, const codec::OpusParams& p,
|
||||||
uint32_t user_id, uint32_t stream_id, bool is_voice);
|
uint32_t user_id, uint32_t stream_id, bool is_voice);
|
||||||
#endif
|
|
||||||
|
|
||||||
// External PCM tap: callback fired once per decoded Opus frame per remote stream, on the
|
// External PCM tap: callback fired once per decoded Opus frame per remote stream, on the
|
||||||
// playback (RT) thread. Matching signature to vc_pcm_sink_cb (cast at the C-ABI boundary).
|
// playback (RT) thread. Matching signature to vc_pcm_sink_cb (cast at the C-ABI boundary).
|
||||||
@@ -254,7 +248,6 @@ class AudioEngine {
|
|||||||
// is samples per channel; total samples written = samples_per_channel * channels.
|
// is samples per channel; total samples written = samples_per_channel * channels.
|
||||||
void inject_capture(int kind, const int16_t* pcm, size_t samples_per_channel, int channels);
|
void inject_capture(int kind, const int16_t* pcm, size_t samples_per_channel, int channels);
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
// TEST-ONLY — exposes the playback mixer without a real ma_device, so tests can verify
|
// TEST-ONLY — exposes the playback mixer without a real ma_device, so tests can verify
|
||||||
// stereo mixing end-to-end (no audio hardware needed). Same logic the real playback
|
// stereo mixing end-to-end (no audio hardware needed). Same logic the real playback
|
||||||
// callback uses; safe to call any time after start() (no ma_device touched).
|
// callback uses; safe to call any time after start() (no ma_device touched).
|
||||||
@@ -312,7 +305,6 @@ class AudioEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_LOOPBACK
|
#ifdef VOICECAT_HAS_LOOPBACK
|
||||||
// TEST-ONLY — drives the loopback accumulator directly with an explicit callback, the
|
// TEST-ONLY — drives the loopback accumulator directly with an explicit callback, the
|
||||||
@@ -354,7 +346,6 @@ class AudioEngine {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
static void capture_data_cb(ma_device*, void*, const void*, ma_uint32);
|
static void capture_data_cb(ma_device*, void*, const void*, ma_uint32);
|
||||||
static void playback_data_cb(ma_device*, void*, const void*, ma_uint32);
|
static void playback_data_cb(ma_device*, void*, const void*, ma_uint32);
|
||||||
void on_capture(const int16_t* pcm, ma_uint32 frames);
|
void on_capture(const int16_t* pcm, ma_uint32 frames);
|
||||||
@@ -403,7 +394,6 @@ class AudioEngine {
|
|||||||
bool loopback_started_ = false;
|
bool loopback_started_ = false;
|
||||||
int loopback_kind_ = 0;
|
int loopback_kind_ = 0;
|
||||||
int loopback_channels_ = 1; // channel count the loopback device was opened with
|
int loopback_channels_ = 1; // channel count the loopback device was opened with
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AudioParams params_{};
|
AudioParams params_{};
|
||||||
@@ -441,9 +431,7 @@ class AudioEngine {
|
|||||||
// Per remote stream (protected by streams_mu_).
|
// Per remote stream (protected by streams_mu_).
|
||||||
struct RemoteStream {
|
struct RemoteStream {
|
||||||
JitterBuffer jitter;
|
JitterBuffer jitter;
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
codec::OpusDecoder decoder;
|
codec::OpusDecoder decoder;
|
||||||
#endif
|
|
||||||
float gain = 1.0f;
|
float gain = 1.0f;
|
||||||
bool mute = false;
|
bool mute = false;
|
||||||
uint32_t playout_ts = 0;
|
uint32_t playout_ts = 0;
|
||||||
@@ -484,9 +472,7 @@ class AudioEngine {
|
|||||||
|
|
||||||
// DRED: pre-allocated scratch for loss recovery. dred_state_ is per-stream; see
|
// DRED: pre-allocated scratch for loss recovery. dred_state_ is per-stream; see
|
||||||
// AudioEngine::dred_dec_ (shared). Allocated in init_recv_stream(); freed in remove_stream().
|
// AudioEngine::dred_dec_ (shared). Allocated in init_recv_stream(); freed in remove_stream().
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
::OpusDRED* dred_state_ = nullptr;
|
::OpusDRED* dred_state_ = nullptr;
|
||||||
#endif
|
|
||||||
std::vector<uint8_t> dred_payload_scratch_; // pre-sized to 4000 bytes
|
std::vector<uint8_t> dred_payload_scratch_; // pre-sized to 4000 bytes
|
||||||
|
|
||||||
// In-band FEC: whether the sender negotiated OPUS_SET_INBAND_FEC for this stream.
|
// In-band FEC: whether the sender negotiated OPUS_SET_INBAND_FEC for this stream.
|
||||||
@@ -574,9 +560,7 @@ class AudioEngine {
|
|||||||
std::atomic<void*> mixed_sink_user_{nullptr};
|
std::atomic<void*> mixed_sink_user_{nullptr};
|
||||||
bool external_playback_ = false;
|
bool external_playback_ = false;
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
::OpusDREDDecoder* dred_dec_ = nullptr; // shared DRED decoder; null if unsupported
|
::OpusDREDDecoder* dred_dec_ = nullptr; // shared DRED decoder; null if unsupported
|
||||||
#endif
|
|
||||||
|
|
||||||
static constexpr int64_t kTalkHangoverMs = 300;
|
static constexpr int64_t kTalkHangoverMs = 300;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
namespace voicecat::codec {
|
namespace voicecat::codec {
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
|
|
||||||
// Map an intended channel/capture sample rate (Hz) to the Opus max-bandwidth constant. The
|
// Map an intended channel/capture sample rate (Hz) to the Opus max-bandwidth constant. The
|
||||||
// codec always runs at 48 kHz internally (docs/voice.md §3); this caps the bandwidth the
|
// codec always runs at 48 kHz internally (docs/voice.md §3); this caps the bandwidth the
|
||||||
// encoder will select so a channel can request narrowband/wideband audio for low-bitrate rooms.
|
// encoder will select so a channel can request narrowband/wideband audio for low-bitrate rooms.
|
||||||
@@ -103,16 +101,4 @@ void OpusDecoder::destroy() {
|
|||||||
if (dec_) { opus_decoder_destroy(dec_); dec_ = nullptr; }
|
if (dec_) { opus_decoder_destroy(dec_); dec_ = nullptr; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_OPUS — stubs
|
|
||||||
|
|
||||||
bool OpusEncoder::init(const OpusParams&) { err_ = "OPUS not compiled in"; return false; }
|
|
||||||
int OpusEncoder::encode(const int16_t*, int, uint8_t*, int) { return -1; }
|
|
||||||
void OpusEncoder::destroy() {}
|
|
||||||
|
|
||||||
bool OpusDecoder::init(const OpusParams&) { err_ = "OPUS not compiled in"; return false; }
|
|
||||||
int OpusDecoder::decode(const uint8_t*, int, int16_t*, int, bool) { return -1; }
|
|
||||||
void OpusDecoder::destroy() {}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_OPUS
|
|
||||||
|
|
||||||
} // namespace voicecat::codec
|
} // namespace voicecat::codec
|
||||||
|
|||||||
@@ -10,9 +10,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
#include <opus/opus.h>
|
#include <opus/opus.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace voicecat::codec {
|
namespace voicecat::codec {
|
||||||
|
|
||||||
@@ -73,11 +71,7 @@ class OpusEncoder {
|
|||||||
const char* error_string() const { return err_; }
|
const char* error_string() const { return err_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
::OpusEncoder* enc_ = nullptr;
|
::OpusEncoder* enc_ = nullptr;
|
||||||
#else
|
|
||||||
void* enc_ = nullptr;
|
|
||||||
#endif
|
|
||||||
int frame_samples_ = 0;
|
int frame_samples_ = 0;
|
||||||
int channels_ = 1;
|
int channels_ = 1;
|
||||||
const char* err_ = nullptr;
|
const char* err_ = nullptr;
|
||||||
@@ -104,10 +98,8 @@ class OpusDecoder {
|
|||||||
// Decode a lost frame using pre-parsed DRED state from the next received packet.
|
// Decode a lost frame using pre-parsed DRED state from the next received packet.
|
||||||
// dred_offset=0 means the frame immediately before the next packet.
|
// dred_offset=0 means the frame immediately before the next packet.
|
||||||
// Returns frame_samples on success, -1 if DRED unavailable or decode failed.
|
// Returns frame_samples on success, -1 if DRED unavailable or decode failed.
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
int decode_dred(::OpusDRED* dred, int32_t dred_offset, int16_t* out_pcm, int max_samples);
|
int decode_dred(::OpusDRED* dred, int32_t dred_offset, int16_t* out_pcm, int max_samples);
|
||||||
::OpusDecoder* raw() const { return dec_; }
|
::OpusDecoder* raw() const { return dec_; }
|
||||||
#endif
|
|
||||||
|
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
@@ -117,11 +109,7 @@ class OpusDecoder {
|
|||||||
const char* error_string() const { return err_; }
|
const char* error_string() const { return err_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
::OpusDecoder* dec_ = nullptr;
|
::OpusDecoder* dec_ = nullptr;
|
||||||
#else
|
|
||||||
void* dec_ = nullptr;
|
|
||||||
#endif
|
|
||||||
int frame_samples_ = 0;
|
int frame_samples_ = 0;
|
||||||
int channels_ = 1;
|
int channels_ = 1;
|
||||||
const char* err_ = nullptr;
|
const char* err_ = nullptr;
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "core/client.h"
|
#include "core/client.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# ifndef WIN32_LEAN_AND_MEAN
|
# ifndef WIN32_LEAN_AND_MEAN
|
||||||
# define WIN32_LEAN_AND_MEAN
|
# define WIN32_LEAN_AND_MEAN
|
||||||
@@ -1867,7 +1865,6 @@ vc_result vc_client::test_inject_capture(uint32_t stream_id, const int16_t* pcm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
vc_result vc_client::list_devices(vc_device_kind kind, vc_device_list* out) {
|
vc_result vc_client::list_devices(vc_device_kind kind, vc_device_list* out) {
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
// Works in any connection state — device pickers need to populate pre-connect.
|
// Works in any connection state — device pickers need to populate pre-connect.
|
||||||
auto devices = voicecat::audio::AudioEngine::enumerate_devices(kind == VC_DEVICE_INPUT);
|
auto devices = voicecat::audio::AudioEngine::enumerate_devices(kind == VC_DEVICE_INPUT);
|
||||||
|
|
||||||
@@ -1885,12 +1882,6 @@ vc_result vc_client::list_devices(vc_device_kind kind, vc_device_list* out) {
|
|||||||
out->items = items;
|
out->items = items;
|
||||||
out->count = devices.size();
|
out->count = devices.size();
|
||||||
return VC_OK;
|
return VC_OK;
|
||||||
#else
|
|
||||||
(void)kind;
|
|
||||||
out->items = nullptr;
|
|
||||||
out->count = 0;
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── M4: channel/user/stream snapshot getters ─────────────────────────────────
|
// ── M4: channel/user/stream snapshot getters ─────────────────────────────────
|
||||||
@@ -2209,102 +2200,3 @@ void vc_client::run_talk_timer() {
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(kTalkPollMs));
|
std::this_thread::sleep_for(std::chrono::milliseconds(kTalkPollMs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
// ── M0 stub implementations ───────────────────────────────────────────────────
|
|
||||||
|
|
||||||
vc_client::vc_client(const vc_config& cfg, vc_callbacks cb) : cfg_(cfg), cb_(cb) {}
|
|
||||||
vc_client::~vc_client() = default;
|
|
||||||
|
|
||||||
void vc_client::emit(const vc_event& ev) const {
|
|
||||||
if (cb_.on_event) cb_.on_event(cb_.user, &ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
vc_result vc_client::connect(const char*, uint16_t) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::disconnect() { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::authenticate_guest(const char*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::authenticate_user(const char*, const char*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::join_channel(uint32_t, const char*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::leave_channel() { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::join_voice() { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::leave_voice() { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::stream_start(const vc_stream_desc&, uint32_t*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::stream_stop(uint32_t) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_input_device(uint32_t, const char*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_capture_channels(uint32_t, uint32_t) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_input_mode(vc_input_mode) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_vad_threshold(float) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_input_gain(float) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_input_noise_reduction(bool) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_push_to_talk(bool) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_self_mute(bool, bool) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_remote_stream(uint32_t, uint32_t, float, bool, bool) {
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::get_remote_stream(uint32_t, uint32_t, vc_remote_stream_state*) {
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::send_text(vc_text_scope, uint32_t, const char*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::list_devices(vc_device_kind, vc_device_list* out) {
|
|
||||||
out->items = nullptr;
|
|
||||||
out->count = 0;
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::get_stream_audio_config(uint32_t, uint32_t, vc_audio_config*) {
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::stream_feed_pcm(uint32_t, const int16_t*, size_t, uint32_t) {
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::set_pcm_sink(vc_pcm_sink_cb, void*) {
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::set_mixed_output_sink(vc_mixed_output_cb, void*) {
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::set_external_playback(bool) {
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::test_inject_capture(uint32_t, const int16_t*, size_t) {
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::list_channels(vc_channel_list* out) {
|
|
||||||
out->items = nullptr;
|
|
||||||
out->count = 0;
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::list_users(vc_user_list* out) {
|
|
||||||
out->items = nullptr;
|
|
||||||
out->count = 0;
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::list_user_streams(uint32_t, vc_stream_summary_list* out) {
|
|
||||||
out->items = nullptr;
|
|
||||||
out->count = 0;
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::confirm_server_identity(bool) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::get_server_identity_display(char*, size_t, size_t* out_len) {
|
|
||||||
if (out_len) *out_len = 0;
|
|
||||||
return VC_ERR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
vc_result vc_client::kick_user(uint32_t, const char*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::ban_user(uint32_t, const char*, uint64_t) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_permission(uint32_t, const vc_permissions*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::set_server_mute(uint32_t, bool, bool) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::move_user(uint32_t, uint32_t) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::create_channel(const vc_channel_info*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::edit_channel(const vc_channel_info*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::delete_channel(uint32_t) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::create_account(const char*, const char*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::reset_password(const char*, const char*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::delete_account(const char*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::list_accounts() { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::get_account_list(vc_account_list*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::get_permissions(vc_permissions*) { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::audio_suspend() { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::audio_resume() { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
vc_result vc_client::audio_restart() { return VC_ERR_NOT_IMPLEMENTED; }
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
#include "voicecat.h"
|
#include "voicecat.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
@@ -29,8 +27,6 @@
|
|||||||
#include "session/session.h"
|
#include "session/session.h"
|
||||||
#include "proto/voicecat.pb.h"
|
#include "proto/voicecat.pb.h"
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
struct vc_client {
|
struct vc_client {
|
||||||
vc_client(const vc_config& cfg, vc_callbacks cb);
|
vc_client(const vc_config& cfg, vc_callbacks cb);
|
||||||
~vc_client();
|
~vc_client();
|
||||||
@@ -118,11 +114,7 @@ struct vc_client {
|
|||||||
vc_result get_permissions(vc_permissions* out);
|
vc_result get_permissions(vc_permissions* out);
|
||||||
|
|
||||||
vc_connection_state state() const {
|
vc_connection_state state() const {
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
return state_net_.load(std::memory_order_acquire);
|
return state_net_.load(std::memory_order_acquire);
|
||||||
#else
|
|
||||||
return state_;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -131,7 +123,6 @@ struct vc_client {
|
|||||||
vc_config cfg_{};
|
vc_config cfg_{};
|
||||||
vc_callbacks cb_{};
|
vc_callbacks cb_{};
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
// ── M1: TCP/TLS control channel ─────────────────────────────────────────────
|
// ── M1: TCP/TLS control channel ─────────────────────────────────────────────
|
||||||
std::atomic<vc_connection_state> state_net_{VC_STATE_DISCONNECTED};
|
std::atomic<vc_connection_state> state_net_{VC_STATE_DISCONNECTED};
|
||||||
|
|
||||||
@@ -339,12 +330,8 @@ struct vc_client {
|
|||||||
// capture callback never allocates or races this pointer.
|
// capture callback never allocates or races this pointer.
|
||||||
std::unique_ptr<voicecat::audio::ApmProcessor> mic_ns_;
|
std::unique_ptr<voicecat::audio::ApmProcessor> mic_ns_;
|
||||||
|
|
||||||
// teardown_voice() is called both from run_io()'s own cleanup (on the io_thread_, when
|
// teardown_voice() is called from run_io() and disconnect() concurrently; this mutex
|
||||||
// the read loop exits) and from disconnect() (on the caller's thread) -- without
|
// makes it idempotent (avoids a double-join race on udp_thread_/talk_timer_thread_).
|
||||||
// serializing those two call sites, both can see udp_thread_/talk_timer_thread_ as
|
|
||||||
// joinable() at the same time and race to join() the same std::thread object (UB; an
|
|
||||||
// intermittent "No such process" std::system_error on Windows is the typical symptom).
|
|
||||||
// This mutex makes teardown_voice() idempotent under concurrent calls.
|
|
||||||
std::mutex teardown_mu_;
|
std::mutex teardown_mu_;
|
||||||
|
|
||||||
// ── io_thread_ entry point ──────────────────────────────────────────────────
|
// ── io_thread_ entry point ──────────────────────────────────────────────────
|
||||||
@@ -432,10 +419,6 @@ struct vc_client {
|
|||||||
// Convenience event emitters.
|
// Convenience event emitters.
|
||||||
void emit_error(vc_result r, const char* text);
|
void emit_error(vc_result r, const char* text);
|
||||||
void emit_disconnected(vc_result r, const char* reason);
|
void emit_disconnected(vc_result r, const char* reason);
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
vc_connection_state state_{VC_STATE_DISCONNECTED};
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VOICECAT_CORE_CLIENT_H
|
#endif // VOICECAT_CORE_CLIENT_H
|
||||||
|
|||||||
@@ -4,13 +4,10 @@
|
|||||||
* Used for Argon2id password hashing (deliberately slow) and TLS handshakes so
|
* Used for Argon2id password hashing (deliberately slow) and TLS handshakes so
|
||||||
* neither blocks the net thread. Real-time audio threads never use this.
|
* neither blocks the net thread. Real-time audio threads never use this.
|
||||||
*
|
*
|
||||||
* Requires VOICECAT_HAS_NET (Asio). Undefined when building without deps.
|
|
||||||
*/
|
*/
|
||||||
#ifndef VOICECAT_CORE_WORKER_POOL_H
|
#ifndef VOICECAT_CORE_WORKER_POOL_H
|
||||||
#define VOICECAT_CORE_WORKER_POOL_H
|
#define VOICECAT_CORE_WORKER_POOL_H
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <asio/thread_pool.hpp>
|
#include <asio/thread_pool.hpp>
|
||||||
#include <asio/post.hpp>
|
#include <asio/post.hpp>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@@ -37,5 +34,4 @@ class WorkerPool {
|
|||||||
|
|
||||||
} // namespace voicecat
|
} // namespace voicecat
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
#endif // VOICECAT_CORE_WORKER_POOL_H
|
#endif // VOICECAT_CORE_WORKER_POOL_H
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "crypto/crypto.h"
|
#include "crypto/crypto.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -406,5 +404,3 @@ long SodiumMediaCrypto::open(const uint8_t* sealed, size_t len, const uint8_t* a
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace voicecat::crypto
|
} // namespace voicecat::crypto
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -12,8 +12,6 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -186,25 +184,4 @@ class SodiumMediaCrypto final : public MediaCrypto {
|
|||||||
|
|
||||||
} // namespace voicecat::crypto
|
} // namespace voicecat::crypto
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET — skeleton stubs
|
|
||||||
|
|
||||||
namespace voicecat::crypto {
|
|
||||||
|
|
||||||
class MediaCrypto {
|
|
||||||
public:
|
|
||||||
virtual ~MediaCrypto() = default;
|
|
||||||
virtual long seal(const uint8_t*, size_t, const uint8_t*, size_t, uint8_t*, size_t) = 0;
|
|
||||||
virtual long open(const uint8_t*, size_t, const uint8_t*, size_t, uint8_t*, size_t) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SodiumMediaCrypto final : public MediaCrypto {
|
|
||||||
public:
|
|
||||||
long seal(const uint8_t*, size_t, const uint8_t*, size_t, uint8_t*, size_t) override { return -1; }
|
|
||||||
long open(const uint8_t*, size_t, const uint8_t*, size_t, uint8_t*, size_t) override { return -1; }
|
|
||||||
uint64_t peek_send_counter() const { return 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace voicecat::crypto
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
#endif // VOICECAT_CRYPTO_CRYPTO_H
|
#endif // VOICECAT_CRYPTO_CRYPTO_H
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "crypto/tofu_store.h"
|
#include "crypto/tofu_store.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@@ -93,5 +91,3 @@ void TofuStore::save() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace voicecat::crypto
|
} // namespace voicecat::crypto
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
#ifndef VOICECAT_CRYPTO_TOFU_STORE_H
|
#ifndef VOICECAT_CRYPTO_TOFU_STORE_H
|
||||||
#define VOICECAT_CRYPTO_TOFU_STORE_H
|
#define VOICECAT_CRYPTO_TOFU_STORE_H
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -62,5 +60,4 @@ class TofuStore {
|
|||||||
|
|
||||||
} // namespace voicecat::crypto
|
} // namespace voicecat::crypto
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
#endif // VOICECAT_CRYPTO_TOFU_STORE_H
|
#endif // VOICECAT_CRYPTO_TOFU_STORE_H
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "net/transport.h"
|
#include "net/transport.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "crypto/crypto.h"
|
#include "crypto/crypto.h"
|
||||||
@@ -483,5 +481,3 @@ asio::ip::udp::endpoint UdpMediaChannel::local_endpoint() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace voicecat::net
|
} // namespace voicecat::net
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -4,9 +4,8 @@
|
|||||||
* Design: docs/architecture.md (Net thread), docs/protocol.md §1 (framing).
|
* Design: docs/architecture.md (Net thread), docs/protocol.md §1 (framing).
|
||||||
* Implementation uses standalone Asio for sockets and timers.
|
* Implementation uses standalone Asio for sockets and timers.
|
||||||
*
|
*
|
||||||
* The real classes are compiled only when VOICECAT_HAS_NET is defined (dev/release/
|
* Design: docs/architecture.md (Net thread), docs/protocol.md §1 (framing).
|
||||||
* server-release — vcpkg deps on). The skeleton-preset stub definitions below keep the
|
* Implementation uses standalone Asio for sockets and timers.
|
||||||
* no-deps build green.
|
|
||||||
*/
|
*/
|
||||||
#ifndef VOICECAT_NET_TRANSPORT_H
|
#ifndef VOICECAT_NET_TRANSPORT_H
|
||||||
#define VOICECAT_NET_TRANSPORT_H
|
#define VOICECAT_NET_TRANSPORT_H
|
||||||
@@ -14,8 +13,6 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#define ASIO_STANDALONE 1
|
#define ASIO_STANDALONE 1
|
||||||
#include <asio.hpp>
|
#include <asio.hpp>
|
||||||
|
|
||||||
@@ -226,21 +223,4 @@ class UdpMediaChannel {
|
|||||||
|
|
||||||
} // namespace voicecat::net
|
} // namespace voicecat::net
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET — skeleton stubs for the dev preset
|
|
||||||
|
|
||||||
namespace voicecat::net {
|
|
||||||
|
|
||||||
class TcpControlChannel {
|
|
||||||
public:
|
|
||||||
bool connected() const { return false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class UdpMediaChannel {
|
|
||||||
public:
|
|
||||||
bool bound() const { return false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace voicecat::net
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
#endif // VOICECAT_NET_TRANSPORT_H
|
#endif // VOICECAT_NET_TRANSPORT_H
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "protocol/envelope.h"
|
#include "protocol/envelope.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include "protocol/protocol.h"
|
#include "protocol/protocol.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@@ -30,5 +28,3 @@ uint64_t next_request_id() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace voicecat::protocol
|
} // namespace voicecat::protocol
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -5,13 +5,10 @@
|
|||||||
* envelopes without touching proto types directly. All callers that do need
|
* envelopes without touching proto types directly. All callers that do need
|
||||||
* the proto types can #include the generated header alongside this one.
|
* the proto types can #include the generated header alongside this one.
|
||||||
*
|
*
|
||||||
* Requires VOICECAT_HAS_NET (protobuf codegen).
|
|
||||||
*/
|
*/
|
||||||
#ifndef VOICECAT_PROTOCOL_ENVELOPE_H
|
#ifndef VOICECAT_PROTOCOL_ENVELOPE_H
|
||||||
#define VOICECAT_PROTOCOL_ENVELOPE_H
|
#define VOICECAT_PROTOCOL_ENVELOPE_H
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -35,5 +32,4 @@ uint64_t next_request_id();
|
|||||||
|
|
||||||
} // namespace voicecat::protocol
|
} // namespace voicecat::protocol
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
#endif // VOICECAT_PROTOCOL_ENVELOPE_H
|
#endif // VOICECAT_PROTOCOL_ENVELOPE_H
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ std::pair<const User*, const Stream*> SessionModel::find_user_by_ssrc(uint32_t s
|
|||||||
return {nullptr, nullptr};
|
return {nullptr, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void copy_channel_audio(Channel& ch, const voicecat::v1::AudioConfig& a) {
|
void copy_channel_audio(Channel& ch, const voicecat::v1::AudioConfig& a) {
|
||||||
@@ -160,6 +158,4 @@ void SessionModel::apply_channel_event(const voicecat::v1::ChannelEvent& ev) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
} // namespace voicecat::session
|
} // namespace voicecat::session
|
||||||
|
|||||||
@@ -12,9 +12,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
#include "proto/voicecat.pb.h"
|
#include "proto/voicecat.pb.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace voicecat::session {
|
namespace voicecat::session {
|
||||||
|
|
||||||
@@ -88,11 +86,9 @@ class SessionModel {
|
|||||||
// Returns {nullptr, nullptr} if not found.
|
// Returns {nullptr, nullptr} if not found.
|
||||||
std::pair<const User*, const Stream*> find_user_by_ssrc(uint32_t ssrc) const;
|
std::pair<const User*, const Stream*> find_user_by_ssrc(uint32_t ssrc) const;
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
void apply_snapshot(const voicecat::v1::ServerStateSnapshot& snap);
|
void apply_snapshot(const voicecat::v1::ServerStateSnapshot& snap);
|
||||||
void apply_user_event(const voicecat::v1::UserEvent& ev);
|
void apply_user_event(const voicecat::v1::UserEvent& ev);
|
||||||
void apply_channel_event(const voicecat::v1::ChannelEvent& ev);
|
void apply_channel_event(const voicecat::v1::ChannelEvent& ev);
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Channel> channels_;
|
std::vector<Channel> channels_;
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ and *how to drive the binaries by hand*.
|
|||||||
| Preset | Binary dir | Deps | Build type | Server | Tools | Tests | Strip | Platform | What it's for |
|
| Preset | Binary dir | Deps | Build type | Server | Tools | Tests | Strip | Platform | What it's for |
|
||||||
|--------|-----------|------|------------|--------|-------|-------|-------|----------|---------------|
|
|--------|-----------|------|------------|--------|-------|-------|-------|----------|---------------|
|
||||||
| `vcpkg-common` | — | vcpkg | — | — | — | — | — | all | Hidden base. Sets the vcpkg toolchain wrapper ([`cmake/voicecat-toolchain.cmake`](../cmake/voicecat-toolchain.cmake)) which auto-resolves the triplet from the host platform. Not used directly. |
|
| `vcpkg-common` | — | vcpkg | — | — | — | — | — | all | Hidden base. Sets the vcpkg toolchain wrapper ([`cmake/voicecat-toolchain.cmake`](../cmake/voicecat-toolchain.cmake)) which auto-resolves the triplet from the host platform. Not used directly. |
|
||||||
| `skeleton` | `build/skeleton` | none | Debug | ON | ON | ON | no | all | The M0 skeleton. Compiles with just a C++20 compiler — no `VCPKG_ROOT` needed. Subsystems are stubs (`VC_ERR_NOT_IMPLEMENTED`). Good for "does the repo even build" sanity checks. Runs 2 tests (smoke + frame_codec). |
|
| `dev` | `build/dev` | vcpkg | Debug | ON | ON | ON | no | all | **The one you actually want.** Day-to-day development: real protocol, crypto, voice, server — everything. Builds server + tools + tests (29 tests). Works on Windows, Linux, and macOS (triplet auto-resolved). |
|
||||||
| `dev` | `build/dev` | vcpkg | Debug | ON | ON | ON | no | all | **The one you actually want.** Day-to-day development: real protocol, crypto, voice, server — everything. Builds server + tools + tests (21 tests). Works on Windows, Linux, and macOS (triplet auto-resolved). |
|
|
||||||
| `release` | `build/release` | vcpkg | Release | ON | ON | ON | no | all | Optimized build with the full test suite. Use to run tests against optimized code, profile, or catch optimizer-sensitive bugs. Symbols kept (not stripped) so stack traces and profiling remain useful. |
|
| `release` | `build/release` | vcpkg | Release | ON | ON | ON | no | all | Optimized build with the full test suite. Use to run tests against optimized code, profile, or catch optimizer-sensitive bugs. Symbols kept (not stripped) so stack traces and profiling remain useful. |
|
||||||
| `server-release` | `build/server-release` | vcpkg | Release | ON | ON | OFF | **yes** | all | Production-shaped build for deployment. Optimized + stripped binaries (`-s`), no tests. This is what you'd ship/run — see [docs/deployment.md](deployment.md). |
|
| `server-release` | `build/server-release` | vcpkg | Release | ON | ON | OFF | **yes** | all | Production-shaped build for deployment. Optimized + stripped binaries (`-s`), no tests. This is what you'd ship/run — see [docs/deployment.md](deployment.md). |
|
||||||
| `windows-client` | `build/windows-client` | vcpkg | Release | OFF | OFF | OFF | no | Windows | Produces a redistributable `voicecat.dll` for the C# WinForms client (M4). Static MinGW runtime — no `libgcc_s_seh-1.dll` etc. See [clients/windows/README.md](../clients/windows/README.md). |
|
| `windows-client` | `build/windows-client` | vcpkg | Release | OFF | OFF | OFF | no | Windows | Produces a redistributable `voicecat.dll` for the C# WinForms client (M4). Static MinGW runtime — no `libgcc_s_seh-1.dll` etc. See [clients/windows/README.md](../clients/windows/README.md). |
|
||||||
@@ -33,10 +32,9 @@ and *how to drive the binaries by hand*.
|
|||||||
| `apple-ios` | `build/apple-ios` | vcpkg | Release | OFF | OFF | OFF | no | macOS→iOS | **Scaffolding** — cross-compiled static `libvoicecat.a` for iOS device (`arm64-ios`). One XCFramework slice. Not yet CI-validated. |
|
| `apple-ios` | `build/apple-ios` | vcpkg | Release | OFF | OFF | OFF | no | macOS→iOS | **Scaffolding** — cross-compiled static `libvoicecat.a` for iOS device (`arm64-ios`). One XCFramework slice. Not yet CI-validated. |
|
||||||
| `apple-ios-sim` | `build/apple-ios-sim` | vcpkg | Release | OFF | OFF | OFF | no | macOS→iOS sim | **Scaffolding** — cross-compiled static `libvoicecat.a` for iOS simulator (`arm64-ios-sim`). One XCFramework slice. Not yet CI-validated. |
|
| `apple-ios-sim` | `build/apple-ios-sim` | vcpkg | Release | OFF | OFF | OFF | no | macOS→iOS sim | **Scaffolding** — cross-compiled static `libvoicecat.a` for iOS simulator (`arm64-ios-sim`). One XCFramework slice. Not yet CI-validated. |
|
||||||
|
|
||||||
So in practice there are three presets that matter for day-to-day work:
|
So in practice there are two presets that matter for day-to-day work:
|
||||||
- **`dev`** — everything: real protocol, real voice, real manual testing. This is the loop you run constantly.
|
- **`dev`** — everything: real protocol, real voice, real manual testing. This is the loop you run constantly.
|
||||||
- **`release`** — same suite, optimized. Run it when you want to check optimized behavior or profile.
|
- **`release`** — same suite, optimized. Run it when you want to check optimized behavior or profile.
|
||||||
- **`skeleton`** — fast no-deps build to confirm the stub path still compiles (CI smoke check).
|
|
||||||
|
|
||||||
The rest are purpose-specific: `server-release` for deployment, `windows-client` for the DLL,
|
The rest are purpose-specific: `server-release` for deployment, `windows-client` for the DLL,
|
||||||
`apple-*` for Apple platform slices.
|
`apple-*` for Apple platform slices.
|
||||||
@@ -63,9 +61,9 @@ The preset set was cleaned up on 2026-06-18 (see `PROGRESS.md`). The old names m
|
|||||||
|
|
||||||
| Old name | New name | Notes |
|
| Old name | New name | Notes |
|
||||||
|----------|----------|-------|
|
|----------|----------|-------|
|
||||||
| `dev` | `skeleton` | Renamed to reflect its actual purpose (no-deps stub smoke check). |
|
|
||||||
| `m1-dev` | `dev` | Renamed — the project is past M5, so milestone-named presets were misleading. This is now the default development preset. |
|
| `m1-dev` | `dev` | Renamed — the project is past M5, so milestone-named presets were misleading. This is now the default development preset. |
|
||||||
| `m2-dev` | *(dropped)* | Was cache-identical to `m1-dev` (same flags, same triplet, only the binary dir differed). Removed. |
|
| `m2-dev` | *(dropped)* | Was cache-identical to `m1-dev` (same flags, same triplet, only the binary dir differed). Removed. |
|
||||||
|
| `skeleton` | *(dropped)* | Removed — was a no-deps stub build mode used during M0. All subsystems are now fully implemented; the stub `#ifdef` scaffolding has been deleted. |
|
||||||
| `server-release` | `server-release` | Unchanged name; now stripped (`-s`) and auto-triplet. |
|
| `server-release` | `server-release` | Unchanged name; now stripped (`-s`) and auto-triplet. |
|
||||||
| *(new)* | `release` | New: optimized build with tests on, symbols kept. |
|
| *(new)* | `release` | New: optimized build with tests on, symbols kept. |
|
||||||
| `windows-client` | `windows-client` | Unchanged name; triplet now auto-resolved. |
|
| `windows-client` | `windows-client` | Unchanged name; triplet now auto-resolved. |
|
||||||
@@ -77,7 +75,7 @@ was run.
|
|||||||
|
|
||||||
## 2. One-time setup for the real-deps presets
|
## 2. One-time setup for the real-deps presets
|
||||||
|
|
||||||
`dev`, `release`, `server-release`, `windows-client`, and `apple-*` all need `VCPKG_ROOT`
|
All presets except `vcpkg-common` need `VCPKG_ROOT`
|
||||||
pointing at a bootstrapped vcpkg checkout:
|
pointing at a bootstrapped vcpkg checkout:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "conn_session.h"
|
#include "conn_session.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -845,5 +843,3 @@ void ConnSession::send_disconnect_and_close(uint32_t code, const std::string& re
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@
|
|||||||
#ifndef VOICECAT_SERVER_CONN_SESSION_H
|
#ifndef VOICECAT_SERVER_CONN_SESSION_H
|
||||||
#define VOICECAT_SERVER_CONN_SESSION_H
|
#define VOICECAT_SERVER_CONN_SESSION_H
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -184,5 +182,4 @@ class ConnSession : public std::enable_shared_from_this<ConnSession> {
|
|||||||
|
|
||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
#endif // VOICECAT_SERVER_CONN_SESSION_H
|
#endif // VOICECAT_SERVER_CONN_SESSION_H
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "db.h"
|
#include "db.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <random>
|
#include <random>
|
||||||
@@ -612,5 +610,3 @@ int64_t Database::now_unix() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
#ifndef VOICECAT_SERVER_DB_H
|
#ifndef VOICECAT_SERVER_DB_H
|
||||||
#define VOICECAT_SERVER_DB_H
|
#define VOICECAT_SERVER_DB_H
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -152,5 +150,4 @@ class Database {
|
|||||||
|
|
||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
#endif // VOICECAT_SERVER_DB_H
|
#endif // VOICECAT_SERVER_DB_H
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "identity.h"
|
#include "identity.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@@ -35,5 +33,3 @@ bool ServerIdentityManager::init(const std::filesystem::path& data_dir,
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
#ifndef VOICECAT_SERVER_IDENTITY_H
|
#ifndef VOICECAT_SERVER_IDENTITY_H
|
||||||
#define VOICECAT_SERVER_IDENTITY_H
|
#define VOICECAT_SERVER_IDENTITY_H
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -36,5 +34,4 @@ class ServerIdentityManager {
|
|||||||
|
|
||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
#endif // VOICECAT_SERVER_IDENTITY_H
|
#endif // VOICECAT_SERVER_IDENTITY_H
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "media_relay.h"
|
#include "media_relay.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -179,5 +177,3 @@ void MediaRelay::on_udp_frame(const uint8_t* data, size_t len,
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
#ifndef VOICECAT_SERVER_MEDIA_RELAY_H
|
#ifndef VOICECAT_SERVER_MEDIA_RELAY_H
|
||||||
#define VOICECAT_SERVER_MEDIA_RELAY_H
|
#define VOICECAT_SERVER_MEDIA_RELAY_H
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#define ASIO_STANDALONE 1
|
#define ASIO_STANDALONE 1
|
||||||
@@ -71,5 +69,4 @@ class MediaRelay {
|
|||||||
|
|
||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
#endif // VOICECAT_SERVER_MEDIA_RELAY_H
|
#endif // VOICECAT_SERVER_MEDIA_RELAY_H
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#define ASIO_STANDALONE 1
|
#define ASIO_STANDALONE 1
|
||||||
#include <asio.hpp>
|
#include <asio.hpp>
|
||||||
#include <asio/signal_set.hpp>
|
#include <asio/signal_set.hpp>
|
||||||
@@ -224,21 +222,3 @@ void Server::stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
namespace voicecat::server {
|
|
||||||
|
|
||||||
int Server::run() {
|
|
||||||
std::fprintf(stderr, "[server] stub: VOICECAT_HAS_NET not defined (build with dev preset)\n");
|
|
||||||
std::printf(" server_name : %s\n", cfg_.server_name.c_str());
|
|
||||||
std::printf(" data_dir : %s\n", cfg_.data_dir.c_str());
|
|
||||||
std::printf(" bind_port : %u\n", cfg_.bind_port);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Server::stop() {}
|
|
||||||
|
|
||||||
} // namespace voicecat::server
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "session_registry.h"
|
#include "session_registry.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -554,5 +552,3 @@ std::optional<voicecat::v1::AudioConfig> SessionRegistry::channel_audio_config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
#ifndef VOICECAT_SERVER_SESSION_REGISTRY_H
|
#ifndef VOICECAT_SERVER_SESSION_REGISTRY_H
|
||||||
#define VOICECAT_SERVER_SESSION_REGISTRY_H
|
#define VOICECAT_SERVER_SESSION_REGISTRY_H
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -223,4 +221,3 @@ class SessionRegistry {
|
|||||||
} // namespace voicecat::server
|
} // namespace voicecat::server
|
||||||
|
|
||||||
#endif // VOICECAT_SERVER_SESSION_REGISTRY_H
|
#endif // VOICECAT_SERVER_SESSION_REGISTRY_H
|
||||||
#endif // VOICECAT_SERVER_SESSION_REGISTRY_H
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ target_link_libraries(test_smoke PRIVATE voicecat::voicecat)
|
|||||||
target_compile_features(test_smoke PRIVATE cxx_std_20)
|
target_compile_features(test_smoke PRIVATE cxx_std_20)
|
||||||
add_test(NAME smoke COMMAND test_smoke)
|
add_test(NAME smoke COMMAND test_smoke)
|
||||||
|
|
||||||
# frame_codec has no third-party deps; runs under both skeleton and dev.
|
|
||||||
# Needs core/src on the include path to reach internal headers (protocol/, session/, etc.).
|
# Needs core/src on the include path to reach internal headers (protocol/, session/, etc.).
|
||||||
add_executable(test_frame_codec test_frame_codec.cpp)
|
add_executable(test_frame_codec test_frame_codec.cpp)
|
||||||
target_link_libraries(test_frame_codec PRIVATE voicecat::voicecat)
|
target_link_libraries(test_frame_codec PRIVATE voicecat::voicecat)
|
||||||
@@ -14,8 +13,7 @@ target_compile_features(test_frame_codec PRIVATE cxx_std_20)
|
|||||||
target_include_directories(test_frame_codec PRIVATE ${CMAKE_SOURCE_DIR}/core/src)
|
target_include_directories(test_frame_codec PRIVATE ${CMAKE_SOURCE_DIR}/core/src)
|
||||||
add_test(NAME frame_codec COMMAND test_frame_codec)
|
add_test(NAME frame_codec COMMAND test_frame_codec)
|
||||||
|
|
||||||
if(VOICECAT_USE_VCPKG_DEPS)
|
set(VC_TEST_INTERNAL_INCLUDES
|
||||||
set(VC_TEST_INTERNAL_INCLUDES
|
|
||||||
${CMAKE_SOURCE_DIR}/core/src
|
${CMAKE_SOURCE_DIR}/core/src
|
||||||
${CMAKE_SOURCE_DIR}/server/src
|
${CMAKE_SOURCE_DIR}/server/src
|
||||||
${CMAKE_BINARY_DIR}/core/generated) # protobuf-generated headers
|
${CMAKE_BINARY_DIR}/core/generated) # protobuf-generated headers
|
||||||
@@ -247,4 +245,3 @@ if(VOICECAT_USE_VCPKG_DEPS)
|
|||||||
target_include_directories(test_recv_noise_reduction PRIVATE ${VC_TEST_INTERNAL_INCLUDES})
|
target_include_directories(test_recv_noise_reduction PRIVATE ${VC_TEST_INTERNAL_INCLUDES})
|
||||||
add_test(NAME recv_noise_reduction COMMAND test_recv_noise_reduction)
|
add_test(NAME recv_noise_reduction COMMAND test_recv_noise_reduction)
|
||||||
set_tests_properties(recv_noise_reduction PROPERTIES TIMEOUT 90)
|
set_tests_properties(recv_noise_reduction PROPERTIES TIMEOUT 90)
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -14,8 +14,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -309,12 +307,3 @@ int main() {
|
|||||||
std::printf("channel_samplerate: %d failure(s)\n", g_failures);
|
std::printf("channel_samplerate: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("channel_samplerate: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -21,8 +21,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -333,12 +331,3 @@ int main() {
|
|||||||
std::printf("channel_user_list_abi: %d failure(s)\n", g_failures);
|
std::printf("channel_user_list_abi: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("channel_user_list_abi: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -210,12 +208,3 @@ int main() {
|
|||||||
std::printf("disconnect_left: %d failure(s)\n", g_failures);
|
std::printf("disconnect_left: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("disconnect_left: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -268,12 +266,3 @@ int main() {
|
|||||||
std::printf("dred_toggle: %d failure(s)\n", g_failures);
|
std::printf("dred_toggle: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("dred_toggle: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -445,12 +443,3 @@ int main() {
|
|||||||
std::printf("external_pcm: %d failure(s)\n", g_failures);
|
std::printf("external_pcm: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("external_pcm: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -318,12 +316,3 @@ int main() {
|
|||||||
std::printf("frame_ms_reframe: %d failure(s)\n", g_failures);
|
std::printf("frame_ms_reframe: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("frame_ms_reframe: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -255,12 +253,3 @@ int main() {
|
|||||||
std::printf("m1_integration: %d failure(s)\n", g_failures);
|
std::printf("m1_integration: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("m1_integration: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -14,8 +14,6 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -53,9 +51,7 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
#include "codec/opus_codec.h"
|
#include "codec/opus_codec.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace voicecat;
|
using namespace voicecat;
|
||||||
using namespace voicecat::net;
|
using namespace voicecat::net;
|
||||||
@@ -441,7 +437,6 @@ int main() {
|
|||||||
constexpr int kFramesToSend = 50;
|
constexpr int kFramesToSend = 50;
|
||||||
constexpr int kFrameSamples = 960; // 20 ms @48 kHz
|
constexpr int kFrameSamples = 960; // 20 ms @48 kHz
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
voicecat::codec::OpusEncoder enc;
|
voicecat::codec::OpusEncoder enc;
|
||||||
{
|
{
|
||||||
voicecat::codec::OpusParams p;
|
voicecat::codec::OpusParams p;
|
||||||
@@ -450,24 +445,17 @@ int main() {
|
|||||||
p.fec = true;
|
p.fec = true;
|
||||||
CHECK(enc.init(p));
|
CHECK(enc.init(p));
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
std::vector<uint8_t> aead_buf(4096); // scratch
|
std::vector<uint8_t> aead_buf(4096); // scratch
|
||||||
|
|
||||||
for (int i = 0; i < kFramesToSend; ++i) {
|
for (int i = 0; i < kFramesToSend; ++i) {
|
||||||
auto pcm = make_sine_frame(i, kFrameSamples);
|
auto pcm = make_sine_frame(i, kFrameSamples);
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
uint8_t opus_buf[1000];
|
uint8_t opus_buf[1000];
|
||||||
int opus_len = enc.encode(pcm.data(), kFrameSamples, opus_buf, sizeof(opus_buf));
|
int opus_len = enc.encode(pcm.data(), kFrameSamples, opus_buf, sizeof(opus_buf));
|
||||||
if (opus_len <= 0) continue;
|
if (opus_len <= 0) continue;
|
||||||
const uint8_t* payload = opus_buf;
|
const uint8_t* payload = opus_buf;
|
||||||
size_t payload_len = static_cast<size_t>(opus_len);
|
size_t payload_len = static_cast<size_t>(opus_len);
|
||||||
#else
|
|
||||||
// Fallback: use raw PCM as synthetic payload
|
|
||||||
const uint8_t* payload = reinterpret_cast<const uint8_t*>(pcm.data());
|
|
||||||
size_t payload_len = pcm.size() * sizeof(int16_t);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Build the voice frame header (AAD).
|
// Build the voice frame header (AAD).
|
||||||
VoiceFrame hdr;
|
VoiceFrame hdr;
|
||||||
@@ -494,9 +482,7 @@ int main() {
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
enc.destroy();
|
enc.destroy();
|
||||||
#endif
|
|
||||||
|
|
||||||
// ── B collects received frames (2s window after last send) ────────────────
|
// ── B collects received frames (2s window after last send) ────────────────
|
||||||
int recv_count = 0, decrypt_ok = 0;
|
int recv_count = 0, decrypt_ok = 0;
|
||||||
@@ -541,12 +527,3 @@ int main() {
|
|||||||
std::printf("m2_voice: %d failure(s)\n", g_failures);
|
std::printf("m2_voice: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("m2_voice: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -387,12 +385,3 @@ int main() {
|
|||||||
std::printf("m3_multistream: %d failure(s)\n", g_failures);
|
std::printf("m3_multistream: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("m3_multistream: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -13,8 +13,6 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -265,12 +263,3 @@ int main() {
|
|||||||
std::printf("m5_admin_accounts: %d failure(s)\n", g_failures);
|
std::printf("m5_admin_accounts: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("m5_admin_accounts: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -12,8 +12,6 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -308,12 +306,3 @@ int main() {
|
|||||||
std::printf("m5_channel_crud: %d failure(s)\n", g_failures);
|
std::printf("m5_channel_crud: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("m5_channel_crud: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -320,12 +318,3 @@ int main() {
|
|||||||
std::printf("m5_kick_ban_move_mute: %d failure(s)\n", g_failures);
|
std::printf("m5_kick_ban_move_mute: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("m5_kick_ban_move_mute: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -12,8 +12,6 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -255,12 +253,3 @@ int main() {
|
|||||||
std::printf("m5_permissions: %d failure(s)\n", g_failures);
|
std::printf("m5_permissions: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("m5_permissions: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -20,14 +20,6 @@ static int g_failures = 0;
|
|||||||
++g_failures; \
|
++g_failures; \
|
||||||
}} while (0)
|
}} while (0)
|
||||||
|
|
||||||
#ifndef VOICECAT_HAS_OPUS
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("opus_codec: VOICECAT_HAS_OPUS not defined — skipped\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static constexpr int kSampleRate = 48000;
|
static constexpr int kSampleRate = 48000;
|
||||||
static constexpr int kFrameMs = 20;
|
static constexpr int kFrameMs = 20;
|
||||||
@@ -145,5 +137,3 @@ int main() {
|
|||||||
std::printf("opus_codec: %d test(s) FAILED\n", g_failures);
|
std::printf("opus_codec: %d test(s) FAILED\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_OPUS
|
|
||||||
|
|||||||
@@ -13,8 +13,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -195,12 +193,3 @@ int main() {
|
|||||||
std::printf("reaper_timeout: %d failure(s)\n", g_failures);
|
std::printf("reaper_timeout: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("reaper_timeout: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* test_smoke — verifies the core links and the C ABI behaves as specified for M0.
|
* test_smoke — verifies the core links and the C ABI behaves as specified.
|
||||||
*
|
*
|
||||||
* This is intentionally a behavior test, not a "does it compile" check: it asserts the
|
* This is intentionally a behavior test, not a "does it compile" check: it asserts the
|
||||||
* documented contract (version present, handle lifecycle, invalid-arg guards, and that
|
* documented contract (version present, handle lifecycle, invalid-arg guards).
|
||||||
* unimplemented calls report VC_ERR_NOT_IMPLEMENTED rather than crashing).
|
|
||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -25,8 +24,6 @@ int main() {
|
|||||||
CHECK(vc_version_string() != nullptr);
|
CHECK(vc_version_string() != nullptr);
|
||||||
CHECK(std::strlen(vc_version_string()) > 0);
|
CHECK(std::strlen(vc_version_string()) > 0);
|
||||||
CHECK(std::strcmp(vc_result_string(VC_OK), "ok") == 0);
|
CHECK(std::strcmp(vc_result_string(VC_OK), "ok") == 0);
|
||||||
CHECK(std::strcmp(vc_result_string(VC_ERR_NOT_IMPLEMENTED), "not implemented") == 0);
|
|
||||||
|
|
||||||
// Null-config create is rejected; valid create yields a handle.
|
// Null-config create is rejected; valid create yields a handle.
|
||||||
vc_callbacks cb{};
|
vc_callbacks cb{};
|
||||||
CHECK(vc_client_create(nullptr, cb) == nullptr);
|
CHECK(vc_client_create(nullptr, cb) == nullptr);
|
||||||
@@ -43,34 +40,26 @@ int main() {
|
|||||||
CHECK(vc_connect(c, nullptr, 1) == VC_ERR_INVALID_ARG);
|
CHECK(vc_connect(c, nullptr, 1) == VC_ERR_INVALID_ARG);
|
||||||
CHECK(vc_send_text(c, VC_TEXT_CHANNEL, 0, nullptr) == VC_ERR_INVALID_ARG);
|
CHECK(vc_send_text(c, VC_TEXT_CHANNEL, 0, nullptr) == VC_ERR_INVALID_ARG);
|
||||||
|
|
||||||
// Under skeleton preset: NOT_IMPLEMENTED. Under dev: VC_OK (async connect).
|
|
||||||
vc_result rc_connect = vc_connect(c, "127.0.0.1", 8384);
|
vc_result rc_connect = vc_connect(c, "127.0.0.1", 8384);
|
||||||
CHECK(rc_connect == VC_ERR_NOT_IMPLEMENTED || rc_connect == VC_OK);
|
CHECK(rc_connect == VC_OK);
|
||||||
|
|
||||||
// Auth before connected (or on a stub) → NOT_CONNECTED or NOT_IMPLEMENTED.
|
// Auth before connected → NOT_CONNECTED.
|
||||||
{
|
{
|
||||||
vc_config cfg2 = cfg;
|
vc_config cfg2 = cfg;
|
||||||
vc_client* c2 = vc_client_create(&cfg2, cb);
|
vc_client* c2 = vc_client_create(&cfg2, cb);
|
||||||
vc_result rc_auth = vc_authenticate_guest(c2, "nick");
|
vc_result rc_auth = vc_authenticate_guest(c2, "nick");
|
||||||
CHECK(rc_auth == VC_ERR_NOT_IMPLEMENTED || rc_auth == VC_ERR_NOT_CONNECTED);
|
CHECK(rc_auth == VC_ERR_NOT_CONNECTED);
|
||||||
vc_client_destroy(c2);
|
vc_client_destroy(c2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// join_channel before connected → NOT_CONNECTED or NOT_IMPLEMENTED.
|
// join_channel before connected → NOT_CONNECTED.
|
||||||
vc_result rc_join = vc_join_channel(c, 1, nullptr);
|
vc_result rc_join = vc_join_channel(c, 1, nullptr);
|
||||||
CHECK(rc_join == VC_ERR_NOT_IMPLEMENTED || rc_join == VC_ERR_NOT_CONNECTED);
|
CHECK(rc_join == VC_ERR_NOT_CONNECTED);
|
||||||
|
|
||||||
vc_device_list dl{};
|
vc_device_list dl{};
|
||||||
vc_result rc_devices = vc_list_devices(c, VC_DEVICE_INPUT, &dl);
|
vc_result rc_devices = vc_list_devices(c, VC_DEVICE_INPUT, &dl);
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
// Never assert count > 0 — a headless CI agent may report zero audio devices.
|
||||||
// Real device enumeration is wired up once miniaudio is linked in (post-M3 follow-up).
|
|
||||||
// Never assert count > 0 here — a headless CI build agent may legitimately report zero
|
|
||||||
// audio devices; only that the call itself succeeded.
|
|
||||||
CHECK(rc_devices == VC_OK);
|
CHECK(rc_devices == VC_OK);
|
||||||
#else
|
|
||||||
CHECK(rc_devices == VC_ERR_NOT_IMPLEMENTED);
|
|
||||||
CHECK(dl.count == 0);
|
|
||||||
#endif
|
|
||||||
vc_free_device_list(&dl);
|
vc_free_device_list(&dl);
|
||||||
|
|
||||||
vc_client_destroy(c);
|
vc_client_destroy(c);
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -374,12 +372,3 @@ int main() {
|
|||||||
std::printf("tofu_flow: %d failure(s)\n", g_failures);
|
std::printf("tofu_flow: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("tofu_flow: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -35,12 +33,8 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
#include "audio/audio_engine.h"
|
#include "audio/audio_engine.h"
|
||||||
#endif
|
|
||||||
#ifdef VOICECAT_HAS_OPUS
|
|
||||||
#include "codec/opus_codec.h"
|
#include "codec/opus_codec.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
// ── Event tracking (same shape as test_m3_multistream.cpp) ──────────────────────
|
// ── Event tracking (same shape as test_m3_multistream.cpp) ──────────────────────
|
||||||
|
|
||||||
@@ -159,7 +153,6 @@ static void test_device_enumeration() {
|
|||||||
for (vc_device_kind kind : {VC_DEVICE_INPUT, VC_DEVICE_OUTPUT}) {
|
for (vc_device_kind kind : {VC_DEVICE_INPUT, VC_DEVICE_OUTPUT}) {
|
||||||
vc_device_list dl{};
|
vc_device_list dl{};
|
||||||
vc_result r = vc_list_devices(c, kind, &dl);
|
vc_result r = vc_list_devices(c, kind, &dl);
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
CHECK(r == VC_OK);
|
CHECK(r == VC_OK);
|
||||||
// Headless CI build agents may legitimately report zero devices — never assert
|
// Headless CI build agents may legitimately report zero devices — never assert
|
||||||
// count > 0, only that the call itself succeeded and the list is well-formed.
|
// count > 0, only that the call itself succeeded and the list is well-formed.
|
||||||
@@ -167,9 +160,6 @@ static void test_device_enumeration() {
|
|||||||
CHECK(dl.items[i].id != nullptr);
|
CHECK(dl.items[i].id != nullptr);
|
||||||
CHECK(dl.items[i].name != nullptr);
|
CHECK(dl.items[i].name != nullptr);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
CHECK(r == VC_ERR_NOT_IMPLEMENTED);
|
|
||||||
#endif
|
|
||||||
vc_free_device_list(&dl);
|
vc_free_device_list(&dl);
|
||||||
vc_free_device_list(&dl); // idempotent — must not crash on a second call
|
vc_free_device_list(&dl); // idempotent — must not crash on a second call
|
||||||
}
|
}
|
||||||
@@ -179,7 +169,6 @@ static void test_device_enumeration() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── 4. Stereo playback mixer (white-box, no audio hardware needed) ──────────────
|
// ── 4. Stereo playback mixer (white-box, no audio hardware needed) ──────────────
|
||||||
#if defined(VOICECAT_HAS_AUDIO) && defined(VOICECAT_HAS_OPUS)
|
|
||||||
static void test_stereo_mix() {
|
static void test_stereo_mix() {
|
||||||
voicecat::audio::AudioEngine engine;
|
voicecat::audio::AudioEngine engine;
|
||||||
voicecat::audio::AudioParams p;
|
voicecat::audio::AudioParams p;
|
||||||
@@ -245,7 +234,7 @@ static void test_stereo_mix() {
|
|||||||
// and mixes — asserting L != R across the frame. A mono-downmixed-then-upmixed bitstream would
|
// and mixes — asserting L != R across the frame. A mono-downmixed-then-upmixed bitstream would
|
||||||
// have L == R. Mirrors test_stereo_mix but routes the encode side through the loopback
|
// have L == R. Mirrors test_stereo_mix but routes the encode side through the loopback
|
||||||
// accumulator path that the fix touches (feed_loopback_for_test → on_loopback's accumulator).
|
// accumulator path that the fix touches (feed_loopback_for_test → on_loopback's accumulator).
|
||||||
#if defined(VOICECAT_HAS_LOOPBACK) && defined(VOICECAT_HAS_OPUS)
|
#if defined(VOICECAT_HAS_LOOPBACK)
|
||||||
static void test_loopback_stereo_capture() {
|
static void test_loopback_stereo_capture() {
|
||||||
voicecat::audio::AudioEngine engine;
|
voicecat::audio::AudioEngine engine;
|
||||||
voicecat::audio::AudioParams p;
|
voicecat::audio::AudioParams p;
|
||||||
@@ -382,14 +371,12 @@ static void test_playout_resync() {
|
|||||||
engine.stop();
|
engine.stop();
|
||||||
std::printf("test_playout_resync: ok (energy=%lld)\n", static_cast<long long>(energy));
|
std::printf("test_playout_resync: ok (energy=%lld)\n", static_cast<long long>(energy));
|
||||||
}
|
}
|
||||||
#endif // VOICECAT_HAS_AUDIO && VOICECAT_HAS_OPUS
|
|
||||||
|
|
||||||
// ── 5. Capture-frame accumulation (white-box, no audio hardware needed) ──────────
|
// ── 5. Capture-frame accumulation (white-box, no audio hardware needed) ──────────
|
||||||
// Regression for the capture-side analogue of the playback ring fix: miniaudio's capture
|
// Regression for the capture-side analogue of the playback ring fix: miniaudio's capture
|
||||||
// callback fires at the hardware period (commonly 480 samples on WASAPI shared mode), while
|
// callback fires at the hardware period (commonly 480 samples on WASAPI shared mode), while
|
||||||
// opus_encode() requires exactly frame_samples_ (960). Sub-frame chunks must be accumulated;
|
// opus_encode() requires exactly frame_samples_ (960). Sub-frame chunks must be accumulated;
|
||||||
// the callback must receive exactly 960-sample frames regardless of input chunk size.
|
// the callback must receive exactly 960-sample frames regardless of input chunk size.
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
static void test_capture_frame_accumulation() {
|
static void test_capture_frame_accumulation() {
|
||||||
voicecat::audio::AudioEngine engine;
|
voicecat::audio::AudioEngine engine;
|
||||||
voicecat::audio::AudioParams p;
|
voicecat::audio::AudioParams p;
|
||||||
@@ -436,7 +423,6 @@ static void test_capture_frame_accumulation() {
|
|||||||
engine.stop();
|
engine.stop();
|
||||||
std::printf("test_capture_frame_accumulation: ok (callbacks=%d)\n", call_count.load());
|
std::printf("test_capture_frame_accumulation: ok (callbacks=%d)\n", call_count.load());
|
||||||
}
|
}
|
||||||
#endif // VOICECAT_HAS_AUDIO
|
|
||||||
|
|
||||||
// ── 2/3. VAD + PTT gate, through the real ABI against a real server ─────────────
|
// ── 2/3. VAD + PTT gate, through the real ABI against a real server ─────────────
|
||||||
static void test_vad_and_ptt_gate() {
|
static void test_vad_and_ptt_gate() {
|
||||||
@@ -601,7 +587,6 @@ static void test_vad_and_ptt_gate() {
|
|||||||
// downmix). Mirrors test_loopback_stereo_capture but routes through the mic capture
|
// downmix). Mirrors test_loopback_stereo_capture but routes through the mic capture
|
||||||
// accumulator (feed_capture_for_test with channels=2) instead of the loopback path.
|
// accumulator (feed_capture_for_test with channels=2) instead of the loopback path.
|
||||||
// This is the headless CI test for the iOS stereo built-in mic feature (Part D).
|
// This is the headless CI test for the iOS stereo built-in mic feature (Part D).
|
||||||
#if defined(VOICECAT_HAS_AUDIO) && defined(VOICECAT_HAS_OPUS)
|
|
||||||
static void test_stereo_mic_capture() {
|
static void test_stereo_mic_capture() {
|
||||||
voicecat::audio::AudioEngine engine;
|
voicecat::audio::AudioEngine engine;
|
||||||
voicecat::audio::AudioParams p;
|
voicecat::audio::AudioParams p;
|
||||||
@@ -674,7 +659,6 @@ static void test_stereo_mic_capture() {
|
|||||||
std::printf("test_stereo_mic_capture: ok (total_diff=%lld, seen_channels=%d)\n",
|
std::printf("test_stereo_mic_capture: ok (total_diff=%lld, seen_channels=%d)\n",
|
||||||
static_cast<long long>(total_diff), seen_channels);
|
static_cast<long long>(total_diff), seen_channels);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// ── 6. Stereo mic capture on a MONO channel (downmix safety) ──────────────────
|
// ── 6. Stereo mic capture on a MONO channel (downmix safety) ──────────────────
|
||||||
// A stereo mic (vc_set_capture_channels=2) can be enabled while on a mono channel. The mic
|
// A stereo mic (vc_set_capture_channels=2) can be enabled while on a mono channel. The mic
|
||||||
@@ -683,7 +667,6 @@ static void test_stereo_mic_capture() {
|
|||||||
// opus_encode makes it read 2× the samples it should (wrong pitch / garbage). This mirrors that
|
// opus_encode makes it read 2× the samples it should (wrong pitch / garbage). This mirrors that
|
||||||
// fold and proves the result is a valid mono bitstream that decodes to the expected averaged
|
// fold and proves the result is a valid mono bitstream that decodes to the expected averaged
|
||||||
// signal, rather than half-length junk.
|
// signal, rather than half-length junk.
|
||||||
#if defined(VOICECAT_HAS_AUDIO) && defined(VOICECAT_HAS_OPUS)
|
|
||||||
static void test_stereo_mic_mono_channel() {
|
static void test_stereo_mic_mono_channel() {
|
||||||
voicecat::codec::OpusParams mono_params;
|
voicecat::codec::OpusParams mono_params;
|
||||||
mono_params.stereo = false; // mono channel — encoder is mono
|
mono_params.stereo = false; // mono channel — encoder is mono
|
||||||
@@ -727,11 +710,9 @@ static void test_stereo_mic_mono_channel() {
|
|||||||
std::printf("test_stereo_mic_mono_channel: ok (opus_len=%d, energy=%lld)\n",
|
std::printf("test_stereo_mic_mono_channel: ok (opus_len=%d, energy=%lld)\n",
|
||||||
opus_len, static_cast<long long>(energy));
|
opus_len, static_cast<long long>(energy));
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
test_device_enumeration();
|
test_device_enumeration();
|
||||||
#if defined(VOICECAT_HAS_AUDIO) && defined(VOICECAT_HAS_OPUS)
|
|
||||||
test_stereo_mix();
|
test_stereo_mix();
|
||||||
#if defined(VOICECAT_HAS_LOOPBACK)
|
#if defined(VOICECAT_HAS_LOOPBACK)
|
||||||
test_loopback_stereo_capture();
|
test_loopback_stereo_capture();
|
||||||
@@ -739,10 +720,7 @@ int main() {
|
|||||||
test_stereo_mic_capture();
|
test_stereo_mic_capture();
|
||||||
test_stereo_mic_mono_channel();
|
test_stereo_mic_mono_channel();
|
||||||
test_playout_resync();
|
test_playout_resync();
|
||||||
#endif
|
|
||||||
#ifdef VOICECAT_HAS_AUDIO
|
|
||||||
test_capture_frame_accumulation();
|
test_capture_frame_accumulation();
|
||||||
#endif
|
|
||||||
test_vad_and_ptt_gate();
|
test_vad_and_ptt_gate();
|
||||||
|
|
||||||
if (g_failures == 0) {
|
if (g_failures == 0) {
|
||||||
@@ -752,12 +730,3 @@ int main() {
|
|||||||
std::printf("vad_ptt_devices: %d failure(s)\n", g_failures);
|
std::printf("vad_ptt_devices: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("vad_ptt_devices: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -9,8 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -253,12 +251,3 @@ int main() {
|
|||||||
std::printf("voice_client_abi: %d failure(s)\n", g_failures);
|
std::printf("voice_client_abi: %d failure(s)\n", g_failures);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::printf("voice_client_abi: SKIP (VOICECAT_HAS_NET not defined)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
@@ -14,8 +14,6 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#ifdef VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
using namespace voicecat::server;
|
using namespace voicecat::server;
|
||||||
|
|
||||||
@@ -141,12 +139,3 @@ int main(int argc, char** argv) {
|
|||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !VOICECAT_HAS_NET
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::fprintf(stderr, "voicecat-admin requires VOICECAT_HAS_NET (build with dev preset)\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // VOICECAT_HAS_NET
|
|
||||||
|
|||||||
Reference in New Issue
Block a user