feat(audio): channel sample_rate caps Opus bandwidth (narrowband/wideband)
Some checks failed
Build Linux Binaries / linux/amd64 (push) Has been cancelled
Build Linux Binaries / linux/arm64 (push) Has been cancelled

The per-channel sample_rate field was inert after pinning the codec to
48 kHz. Make it meaningful without changing the 48 kHz clock: carry it as
OpusParams::max_bandwidth_hz and apply OPUS_SET_MAX_BANDWIDTH in
OpusEncoder::init (8000->narrowband, 16000->wideband, 24000->super-wideband,
48000->full). A low-bitrate room can now shed out-of-band content while
every endpoint keeps a single 48 kHz clock.

Make sample_rate channel-authoritative on the server: conn_session no
longer overrides effective sample_rate with the client's always-48000
request (it now behaves like frame_ms/mode). vc_get_stream_audio_config
reports the channel's configured rate for own streams too.

New ctest channel_samplerate: a 7 kHz tone is attenuated ~1000x on an
8 kHz (narrowband) channel vs a 48 kHz (full-band) channel, proving the cap
is in effect. ctest --preset dev 26/26.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 16:56:49 +02:00
parent a460009a2f
commit e155e342f4
9 changed files with 379 additions and 15 deletions

View File

@@ -531,21 +531,20 @@ void ConnSession::handle_stream_announce(uint64_t req_id,
res->set_ssrc(ssrc);
// Per-channel AudioConfig is authoritative (docs/voice.md §3): the channel's mode/
// frame_ms/application/fec/dtx/complexity/expected_packet_loss apply to every stream
// announced into it, regardless of kind. bitrate_bps is clamped (not overridden) to the
// channel's ceiling so a client may still request less. sample_rate stays
// client-requested-or-48000 — everything runs at 48kHz internally per voice.md §3.
// sample_rate/frame_ms/application/fec/dtx/complexity/expected_packet_loss apply to every
// stream announced into it, regardless of kind. bitrate_bps is clamped (not overridden) to
// the channel's ceiling so a client may still request less. sample_rate is taken from the
// channel verbatim (copied via *eff below) — the codec always runs at 48kHz internally, but
// a sub-48k value caps the encoder's audio bandwidth (narrowband/wideband; see
// OpusEncoder::init), so it's a channel policy, not a client choice.
auto* eff = res->mutable_effective_audio();
auto chan_cfg = registry_->channel_audio_config(registry_->user_channel(user_id_.load()));
uint32_t requested_bps =
msg.has_requested_audio() ? msg.requested_audio().bitrate_bps() : 0;
uint32_t requested_rate =
msg.has_requested_audio() ? msg.requested_audio().sample_rate() : 0;
if (chan_cfg) {
*eff = *chan_cfg;
eff->set_bitrate_bps(requested_bps > 0 ? std::min(requested_bps, chan_cfg->bitrate_bps())
: chan_cfg->bitrate_bps());
eff->set_sample_rate(requested_rate > 0 ? requested_rate : 48000);
} else if (msg.has_requested_audio()) {
*eff = msg.requested_audio();
} else {