feat(windows): UI overhaul -- toolbar, unified log, PM windows, channel counts, output volume
- Voice actions (Join Voice, Share Screen Audio) moved to a ToolStrip toolbar and a new Voice menu in the menu bar; removed from the bottom voice panel - Activity log and chat log collapsed into a single RichTextBox (rtbLog); activity events appear in gray, chat messages in default color - Private messaging reworked: each conversation opens in its own modeless PrivateMessageForm instead of sharing the main chat log via a scope dropdown; cboScope removed; main compose bar always sends to the current channel - New "Messages -> New Private Message..." menu item (Ctrl+P) opens a UserPickerDialog listing all connected server users (not just the current channel) so you can PM anyone on the server - Channel tree now shows live user counts, e.g. "General (3)" -- counts sourced from the existing _users dictionary which already tracks all server users with channel IDs - Global output volume slider (TrackBar, 0-100, default 80) added to the right panel; wired to new vc_set_output_volume C ABI function that applies a master gain multiplier in the audio engine playback callback after mixing all streams - vc_set_output_volume added end-to-end: voicecat.h, audio_engine.h/.cpp, client.h/.cpp, voicecat.cpp, NativeMethods.cs, VoiceCatClient.cs - Documented Windows PowerShell ctest requirement in AGENTS.md and CLAUDE.md: MinGW binaries exit 0xc0000139 in Git Bash; always run ctest/.exe via PowerShell 22/22 ctest green (PowerShell); dotnet build 0 warnings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -659,8 +659,11 @@ void AudioEngine::on_playback(int16_t* out, ma_uint32 frames) {
|
||||
}
|
||||
}
|
||||
|
||||
for (ma_uint32 i = 0; i < frames * pb_channels; ++i)
|
||||
out[i] = static_cast<int16_t>(std::clamp(mix[i], -32768, 32767));
|
||||
const float ovol = output_volume_.load(std::memory_order_relaxed);
|
||||
for (ma_uint32 i = 0; i < frames * pb_channels; ++i) {
|
||||
int32_t s = static_cast<int32_t>(static_cast<float>(mix[i]) * ovol);
|
||||
out[i] = static_cast<int16_t>(std::clamp(s, -32768, 32767));
|
||||
}
|
||||
#else
|
||||
(void)out;
|
||||
(void)frames;
|
||||
|
||||
@@ -163,6 +163,9 @@ class AudioEngine {
|
||||
// Called by the net thread when a decoded voice frame arrives for a remote stream.
|
||||
void push_recv_frame(uint32_t ssrc, JitterBuffer::Frame f);
|
||||
|
||||
// Global output volume applied after mixing all streams (default 1.0). Thread-safe.
|
||||
void set_output_volume(float gain) { output_volume_.store(gain, std::memory_order_relaxed); }
|
||||
|
||||
// Per-stream receive-side controls (safe from any thread).
|
||||
void set_stream_gain(uint32_t ssrc, float gain); // 0.0–2.0, default 1.0
|
||||
void set_stream_mute(uint32_t ssrc, bool mute);
|
||||
@@ -337,7 +340,8 @@ class AudioEngine {
|
||||
|
||||
AudioParams params_{};
|
||||
CaptureCallback capture_cb_;
|
||||
std::atomic<bool> running_{false};
|
||||
std::atomic<bool> running_{false};
|
||||
std::atomic<float> output_volume_{1.0f};
|
||||
|
||||
// Capture-side frame accumulators: miniaudio fires the capture (and loopback) callback at
|
||||
// whatever period the hardware/driver chooses — commonly 480 samples (10 ms) on WASAPI
|
||||
|
||||
@@ -1365,6 +1365,11 @@ vc_result vc_client::set_self_mute(bool mic_muted, bool deafened) {
|
||||
return VC_OK;
|
||||
}
|
||||
|
||||
vc_result vc_client::set_output_volume(float gain) {
|
||||
audio_engine_.set_output_volume(gain < 0.0f ? 0.0f : gain);
|
||||
return VC_OK;
|
||||
}
|
||||
|
||||
vc_result vc_client::set_remote_stream(uint32_t user_id, uint32_t stream_id, float gain,
|
||||
bool muted, bool noise_reduction) {
|
||||
if (state_net_.load(std::memory_order_acquire) != VC_STATE_CONNECTED) return VC_ERR_NOT_CONNECTED;
|
||||
|
||||
@@ -54,6 +54,7 @@ struct vc_client {
|
||||
vc_result set_vad_threshold(float threshold);
|
||||
vc_result set_push_to_talk(bool active);
|
||||
vc_result set_self_mute(bool mic_muted, bool deafened);
|
||||
vc_result set_output_volume(float gain);
|
||||
vc_result set_remote_stream(uint32_t user_id, uint32_t stream_id, float gain, bool muted,
|
||||
bool noise_reduction);
|
||||
vc_result get_remote_stream(uint32_t user_id, uint32_t stream_id,
|
||||
|
||||
@@ -117,6 +117,11 @@ vc_result vc_set_self_mute(vc_client* c, int mic_muted, int deafened) {
|
||||
return c->set_self_mute(mic_muted != 0, deafened != 0);
|
||||
}
|
||||
|
||||
vc_result vc_set_output_volume(vc_client* c, float gain) {
|
||||
if (c == nullptr) return VC_ERR_INVALID_ARG;
|
||||
return c->set_output_volume(gain);
|
||||
}
|
||||
|
||||
vc_result vc_set_remote_stream(vc_client* c, uint32_t user_id, uint32_t stream_id, float gain,
|
||||
int muted, int noise_reduction) {
|
||||
if (c == nullptr) return VC_ERR_INVALID_ARG;
|
||||
|
||||
Reference in New Issue
Block a user