Receive: Config of decode sample rate/channels (#265)
* Receive: Config of decode sample rate/channels This PR allows for dynamic configuration of the output sample rate and channel count of received Opus audio. Users who rely on supported formats should no longer need to manually resample & downmix audio decoded from SSRCs in a call. Opus exposes tuples of (Mono, Stereo) x (8, 12, 16, 24, 48)kHz. Changing this at runtime (mid-call) may cause some audio glitches, as decoder state must be reconstructed from scratch for all affected SSRCs. * Fix doc typo, consistent naming with MixMode.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
use audiopus::{Channels as OpusChannels, SampleRate as OpusRate};
|
||||
|
||||
/// Decode behaviour for received RTP packets within the driver.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum DecodeMode {
|
||||
/// Packets received from Discord are handed over to events without any
|
||||
@@ -24,3 +26,68 @@ impl DecodeMode {
|
||||
self != DecodeMode::Pass
|
||||
}
|
||||
}
|
||||
|
||||
/// The channel layout of output audio when using [`DecodeMode::Decode`].
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum Channels {
|
||||
/// Decode received audio packets into a single channel.
|
||||
Mono,
|
||||
/// Decode received audio packets into two interleaved channels.
|
||||
///
|
||||
/// Received mono packets' samples will automatically be duplicated across
|
||||
/// both channels.
|
||||
///
|
||||
/// The default choice.
|
||||
#[default]
|
||||
Stereo,
|
||||
}
|
||||
|
||||
impl Channels {
|
||||
pub(crate) fn channels(self) -> usize {
|
||||
match self {
|
||||
Channels::Mono => 1,
|
||||
Channels::Stereo => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Channels> for OpusChannels {
|
||||
fn from(value: Channels) -> Self {
|
||||
match value {
|
||||
Channels::Mono => OpusChannels::Mono,
|
||||
Channels::Stereo => OpusChannels::Stereo,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The sample rate of output audio when using [`DecodeMode::Decode`].
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum SampleRate {
|
||||
/// Decode to a sample rate of 8kHz.
|
||||
Hz8000,
|
||||
/// Decode to a sample rate of 12kHz.
|
||||
Hz12000,
|
||||
/// Decode to a sample rate of 16kHz.
|
||||
Hz16000,
|
||||
/// Decode to a sample rate of 24kHz.
|
||||
Hz24000,
|
||||
/// Decode to a sample rate of 48kHz.
|
||||
///
|
||||
/// The preferred option for encoding/decoding at or above CD quality.
|
||||
#[default]
|
||||
Hz48000,
|
||||
}
|
||||
|
||||
impl From<SampleRate> for OpusRate {
|
||||
fn from(value: SampleRate) -> Self {
|
||||
match value {
|
||||
SampleRate::Hz8000 => OpusRate::Hz8000,
|
||||
SampleRate::Hz12000 => OpusRate::Hz12000,
|
||||
SampleRate::Hz16000 => OpusRate::Hz16000,
|
||||
SampleRate::Hz24000 => OpusRate::Hz24000,
|
||||
SampleRate::Hz48000 => OpusRate::Hz48000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ use connection::error::{Error, Result};
|
||||
pub use crypto::CryptoMode;
|
||||
pub(crate) use crypto::CryptoState;
|
||||
#[cfg(feature = "receive")]
|
||||
pub use decode_mode::DecodeMode;
|
||||
pub use decode_mode::*;
|
||||
pub use mix_mode::MixMode;
|
||||
pub use scheduler::{
|
||||
Config as SchedulerConfig,
|
||||
|
||||
@@ -66,7 +66,13 @@ impl UdpRx {
|
||||
*interconnect = i;
|
||||
},
|
||||
Ok(UdpRxMessage::SetConfig(c)) => {
|
||||
let old_coder = (self.config.decode_channels, self.config.decode_sample_rate);
|
||||
let new_coder = (c.decode_channels, c.decode_sample_rate);
|
||||
self.config = c;
|
||||
|
||||
if old_coder != new_coder {
|
||||
self.decoder_map.values_mut().for_each(|v| v.reconfigure_decoder(&self.config));
|
||||
}
|
||||
},
|
||||
Err(flume::RecvError::Disconnected) => break,
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::*;
|
||||
use crate::{
|
||||
constants::*,
|
||||
driver::{
|
||||
tasks::error::{Error, Result},
|
||||
Channels,
|
||||
DecodeMode,
|
||||
},
|
||||
events::context_data::{RtpData, VoiceData},
|
||||
@@ -11,7 +11,6 @@ use audiopus::{
|
||||
coder::Decoder as OpusDecoder,
|
||||
error::{Error as OpusError, ErrorCode},
|
||||
packet::Packet as OpusPacket,
|
||||
Channels,
|
||||
};
|
||||
use discortp::{rtp::RtpExtensionPacket, Packet, PacketSize};
|
||||
use tracing::{error, warn};
|
||||
@@ -24,6 +23,7 @@ pub struct SsrcState {
|
||||
decode_size: PacketDecodeSize,
|
||||
pub(crate) prune_time: Instant,
|
||||
pub(crate) disconnected: bool,
|
||||
channels: Channels,
|
||||
}
|
||||
|
||||
impl SsrcState {
|
||||
@@ -33,14 +33,27 @@ impl SsrcState {
|
||||
Self {
|
||||
playout_buffer: PlayoutBuffer::new(playout_capacity, pkt.get_sequence().0),
|
||||
crypto_mode,
|
||||
decoder: OpusDecoder::new(SAMPLE_RATE, Channels::Stereo)
|
||||
.expect("Failed to create new Opus decoder for source."),
|
||||
decoder: OpusDecoder::new(
|
||||
config.decode_sample_rate.into(),
|
||||
config.decode_channels.into(),
|
||||
)
|
||||
.expect("Failed to create new Opus decoder for source."),
|
||||
decode_size: PacketDecodeSize::TwentyMillis,
|
||||
prune_time: Instant::now() + config.decode_state_timeout,
|
||||
disconnected: false,
|
||||
channels: config.decode_channels,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reconfigure_decoder(&mut self, config: &Config) {
|
||||
self.decoder = OpusDecoder::new(
|
||||
config.decode_sample_rate.into(),
|
||||
config.decode_channels.into(),
|
||||
)
|
||||
.expect("Failed to create new Opus decoder for source.");
|
||||
self.channels = config.decode_channels;
|
||||
}
|
||||
|
||||
pub fn store_packet(&mut self, packet: StoredPacket, config: &Config) {
|
||||
self.playout_buffer.store_packet(packet, config);
|
||||
}
|
||||
@@ -160,7 +173,7 @@ impl SsrcState {
|
||||
Ok(audio_len) => {
|
||||
// Decoding to stereo: audio_len refers to sample count irrespective of channel count.
|
||||
// => multiply by number of channels.
|
||||
out.truncate(2 * audio_len);
|
||||
out.truncate(self.channels.channels() * audio_len);
|
||||
|
||||
break;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user