use super::types::*;
use crate::types::{Position3D, SpatialResult};
use crate::{Error, Result};
use std::collections::{HashMap, HashSet, VecDeque};
use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
impl Default for MultiUserConfig {
fn default() -> Self {
Self {
max_users_per_room: 50,
max_sources_per_user: 5,
sync_interval_ms: 50,
max_latency_ms: 100.0,
voice_activity_threshold: 0.3,
audio_quality: 0.8,
position_interpolation: true,
max_audio_distance: 100.0,
attenuation_curve: MultiUserAttenuationCurve::InverseDistance,
privacy_settings: PrivacySettings::default(),
bandwidth_settings: BandwidthSettings::default(),
}
}
}
impl Default for PrivacySettings {
fn default() -> Self {
Self {
encryption_enabled: true,
recording_allowed: false,
mute_controls_enabled: true,
spatial_zones_enabled: true,
permission_system: PermissionSystem::default(),
anonymization: AnonymizationSettings::default(),
}
}
}
impl Default for PermissionSystem {
fn default() -> Self {
let mut role_permissions = HashMap::new();
role_permissions.insert(UserRole::Guest, vec![Permission::Speak, Permission::Move]);
role_permissions.insert(
UserRole::Participant,
vec![
Permission::Speak,
Permission::Move,
Permission::CreateSources,
],
);
role_permissions.insert(
UserRole::Presenter,
vec![
Permission::Speak,
Permission::Move,
Permission::CreateSources,
Permission::Broadcast,
],
);
role_permissions.insert(
UserRole::Moderator,
vec![
Permission::Speak,
Permission::Move,
Permission::CreateSources,
Permission::MuteOthers,
Permission::ModifyRoom,
Permission::Moderate,
],
);
role_permissions.insert(
UserRole::Administrator,
vec![
Permission::Speak,
Permission::Move,
Permission::CreateSources,
Permission::MuteOthers,
Permission::KickUsers,
Permission::ModifyRoom,
Permission::Record,
Permission::AccessPrivateZones,
Permission::Broadcast,
Permission::Moderate,
],
);
role_permissions.insert(UserRole::Observer, vec![Permission::Move]);
Self {
rbac_enabled: true,
default_role: UserRole::Participant,
role_permissions,
}
}
}
impl Default for AnonymizationSettings {
fn default() -> Self {
Self {
anonymous_ids: false,
position_obfuscation: false,
temporal_obfuscation: false,
voice_modulation: false,
}
}
}
impl Default for BandwidthSettings {
fn default() -> Self {
Self {
adaptive_bitrate: true,
max_bandwidth_kbps: 128,
compression_level: 5,
proximity_quality_scaling: true,
low_bandwidth_mode: LowBandwidthMode {
enabled: false,
sample_rate: 16000,
bit_depth: 16,
max_streams: 5,
disable_spatial_effects: true,
},
}
}
}
impl MultiUserEnvironment {
pub fn new(config: MultiUserConfig) -> Result<Self> {
let sync_manager = SynchronizationManager::new();
let audio_processor = MultiUserAudioProcessor::new(&config)?;
Ok(Self {
config,
users: Arc::new(RwLock::new(HashMap::new())),
sources: Arc::new(RwLock::new(HashMap::new())),
zones: Arc::new(RwLock::new(HashMap::new())),
sync_manager,
audio_processor,
metrics: Arc::new(RwLock::new(MultiUserMetrics::default())),
event_history: Arc::new(RwLock::new(VecDeque::new())),
})
}
pub fn add_user(&self, user: MultiUserUser) -> Result<()> {
let user_id = user.id.clone();
let position = user.position;
{
let users = self.users.read().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire read lock on users: {}", e))
})?;
if users.len() >= self.config.max_users_per_room {
return Err(Error::LegacyProcessing(
"Maximum users per room exceeded".to_string(),
));
}
}
{
let mut users = self.users.write().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire write lock on users: {}", e))
})?;
users.insert(user_id.clone(), user);
}
self.record_event(MultiUserEvent::UserJoined {
user_id,
timestamp: SystemTime::now(),
position,
});
Ok(())
}
pub fn remove_user(&self, user_id: &UserId, reason: DisconnectReason) -> Result<()> {
{
let mut users = self.users.write().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire write lock on users: {}", e))
})?;
users.remove(user_id);
}
self.record_event(MultiUserEvent::UserLeft {
user_id: user_id.clone(),
timestamp: SystemTime::now(),
reason,
});
Ok(())
}
pub fn update_user_position(
&mut self,
user_id: &UserId,
position: Position3D,
orientation: [f32; 4],
) -> Result<()> {
let old_position;
let old_timestamp;
let calculated_velocity;
{
let mut users = self.users.write().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire write lock on users: {}", e))
})?;
if let Some(user) = users.get_mut(user_id) {
old_position = user.position;
old_timestamp = user.last_update;
let current_time = Instant::now();
let time_delta = current_time.duration_since(old_timestamp).as_secs_f32();
if time_delta > 0.0 {
let position_delta = Position3D::new(
position.x - old_position.x,
position.y - old_position.y,
position.z - old_position.z,
);
calculated_velocity = Position3D::new(
position_delta.x / time_delta,
position_delta.y / time_delta,
position_delta.z / time_delta,
);
user.velocity = calculated_velocity;
} else {
calculated_velocity = user.velocity;
}
user.position = position;
user.orientation = orientation;
user.last_update = current_time;
} else {
return Err(Error::LegacyPosition(format!("User {user_id} not found")));
}
}
let estimated_latency = self
.sync_manager
.position_interpolator
.estimate_latency(user_id)
.unwrap_or(0.0);
self.sync_manager.position_interpolator.add_position_sample(
user_id,
PositionSnapshot {
position,
orientation,
velocity: calculated_velocity,
timestamp: Instant::now(),
latency_ms: estimated_latency,
},
);
self.record_event(MultiUserEvent::UserMoved {
user_id: user_id.clone(),
timestamp: SystemTime::now(),
old_position,
new_position: position,
});
Ok(())
}
pub fn add_friend(&self, user_id: &UserId, friend_id: &UserId) -> Result<()> {
if user_id == friend_id {
return Err(Error::LegacyPosition(
"Cannot add self as friend".to_string(),
));
}
{
let mut users = self.users.write().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire write lock on users: {}", e))
})?;
if !users.contains_key(user_id) {
return Err(Error::LegacyPosition(format!("User {user_id} not found")));
}
if !users.contains_key(friend_id) {
return Err(Error::LegacyPosition(format!(
"Friend {friend_id} not found"
)));
}
if let Some(user) = users.get_mut(user_id) {
user.friends.insert(friend_id.clone());
}
if let Some(friend) = users.get_mut(friend_id) {
friend.friends.insert(user_id.clone());
}
}
self.record_event(MultiUserEvent::UserMoved {
user_id: user_id.clone(),
timestamp: SystemTime::now(),
old_position: Position3D::new(0.0, 0.0, 0.0), new_position: Position3D::new(0.0, 0.0, 0.0), });
Ok(())
}
pub fn remove_friend(&self, user_id: &UserId, friend_id: &UserId) -> Result<()> {
{
let mut users = self.users.write().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire write lock on users: {}", e))
})?;
if let Some(user) = users.get_mut(user_id) {
user.friends.remove(friend_id);
}
if let Some(friend) = users.get_mut(friend_id) {
friend.friends.remove(user_id);
}
}
Ok(())
}
pub fn are_friends(&self, user_id: &UserId, friend_id: &UserId) -> Result<bool> {
let users = self.users.read().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire read lock on users: {}", e))
})?;
if let Some(user) = users.get(user_id) {
Ok(user.friends.contains(friend_id))
} else {
Err(Error::LegacyPosition(format!("User {user_id} not found")))
}
}
pub fn get_friends(&self, user_id: &UserId) -> Result<Vec<UserId>> {
let users = self.users.read().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire read lock on users: {}", e))
})?;
if let Some(user) = users.get(user_id) {
Ok(user.friends.iter().cloned().collect())
} else {
Err(Error::LegacyPosition(format!("User {user_id} not found")))
}
}
pub fn add_audio_source(&self, source: MultiUserAudioSource) -> Result<()> {
let source_id = source.id.clone();
let user_id = source.owner_id.clone();
let source_type = source.source_type;
{
let mut sources = self.sources.write().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire write lock on sources: {}", e))
})?;
sources.insert(source_id.clone(), source);
}
self.record_event(MultiUserEvent::SourceCreated {
source_id,
user_id,
timestamp: SystemTime::now(),
source_type,
});
Ok(())
}
pub fn remove_audio_source(&self, source_id: &SourceId, reason: &str) -> Result<()> {
{
let mut sources = self.sources.write().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire write lock on sources: {}", e))
})?;
sources.remove(source_id);
}
self.record_event(MultiUserEvent::SourceRemoved {
source_id: source_id.clone(),
timestamp: SystemTime::now(),
reason: reason.to_string(),
});
Ok(())
}
pub fn process_audio(&mut self) -> Result<HashMap<UserId, Vec<f32>>> {
let users = self.users.read().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire read lock on users: {}", e))
})?;
let mut output_buffers = HashMap::new();
for (user_id, user) in users.iter() {
let audio_buffer =
self.audio_processor
.process_for_user(user, &users, &self.sources)?;
output_buffers.insert(user_id.clone(), audio_buffer);
}
Ok(output_buffers)
}
pub fn metrics(&self) -> MultiUserMetrics {
self.metrics
.read()
.expect("Failed to acquire read lock on metrics for retrieval")
.clone()
}
pub fn create_zone(&self, zone: SpatialZone) -> Result<()> {
let mut zones = self.zones.write().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire write lock on zones: {}", e))
})?;
zones.insert(zone.id.clone(), zone);
Ok(())
}
pub fn check_permission(&self, user_id: &UserId, permission: Permission) -> Result<bool> {
let users = self.users.read().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire read lock on users: {}", e))
})?;
if let Some(user) = users.get(user_id) {
if let Some(permissions) = self
.config
.privacy_settings
.permission_system
.role_permissions
.get(&user.role)
{
Ok(permissions.contains(&permission))
} else {
Ok(false)
}
} else {
Err(Error::LegacyPosition(format!("User {user_id} not found")))
}
}
fn record_event(&self, event: MultiUserEvent) {
let mut history = self
.event_history
.write()
.expect("Failed to acquire write lock on event history");
history.push_back(event);
if history.len() > 1000 {
history.pop_front();
}
}
}
impl SynchronizationManager {
pub fn new() -> Self {
Self {
clock: Arc::new(RwLock::new(SynchronizedClock::new())),
position_interpolator: PositionInterpolator::new(),
latency_compensator: LatencyCompensator::new(),
}
}
}
impl Default for SynchronizationManager {
fn default() -> Self {
Self::new()
}
}
impl SynchronizedClock {
pub fn new() -> Self {
Self {
local_time: Instant::now(),
time_offset_ms: 0,
sync_accuracy_ms: 0.0,
last_sync: Instant::now(),
}
}
pub fn current_time(&self) -> Instant {
self.local_time
}
}
impl Default for SynchronizedClock {
fn default() -> Self {
Self::new()
}
}
impl PositionInterpolator {
pub fn new() -> Self {
Self {
position_histories: HashMap::new(),
interpolation_method: InterpolationMethod::Linear,
prediction_horizon_ms: 50.0,
}
}
pub fn add_position_sample(&mut self, user_id: &UserId, snapshot: PositionSnapshot) {
let history = self.position_histories.entry(user_id.clone()).or_default();
history.push_back(snapshot);
if history.len() > 10 {
history.pop_front();
}
}
pub fn interpolate_position(
&self,
user_id: &UserId,
target_time: Instant,
) -> Option<Position3D> {
let history = self.position_histories.get(user_id)?;
if history.len() < 1 {
return None;
}
if history.len() == 1 {
return history.back().map(|s| s.position);
}
match self.interpolation_method {
InterpolationMethod::Linear => self.linear_interpolation(history, target_time),
InterpolationMethod::CubicSpline => {
self.cubic_spline_interpolation(history, target_time)
}
InterpolationMethod::Kalman => self.kalman_interpolation(history, target_time),
InterpolationMethod::Physics => self.physics_interpolation(history, target_time),
}
}
fn linear_interpolation(
&self,
history: &VecDeque<PositionSnapshot>,
target_time: Instant,
) -> Option<Position3D> {
let mut before_sample = None;
let mut after_sample = None;
for (i, sample) in history.iter().enumerate() {
if sample.timestamp <= target_time {
before_sample = Some(sample);
}
if sample.timestamp >= target_time && after_sample.is_none() {
after_sample = Some(sample);
break;
}
}
match (before_sample, after_sample) {
(Some(before), Some(after)) if before.timestamp != after.timestamp => {
let total_duration = after
.timestamp
.duration_since(before.timestamp)
.as_secs_f32();
let elapsed_duration = target_time.duration_since(before.timestamp).as_secs_f32();
let t = elapsed_duration / total_duration;
Some(Position3D::new(
before.position.x + t * (after.position.x - before.position.x),
before.position.y + t * (after.position.y - before.position.y),
before.position.z + t * (after.position.z - before.position.z),
))
}
(Some(sample), _) => Some(sample.position),
(_, Some(sample)) => Some(sample.position),
_ => history.back().map(|s| s.position),
}
}
fn cubic_spline_interpolation(
&self,
history: &VecDeque<PositionSnapshot>,
target_time: Instant,
) -> Option<Position3D> {
if history.len() < 4 {
return self.linear_interpolation(history, target_time);
}
let samples: Vec<_> = history.iter().collect();
let n = samples.len();
for i in 1..n {
if samples[i].timestamp >= target_time {
let p0 = if i >= 2 { samples[i - 2] } else { samples[0] };
let p1 = samples[i - 1];
let p2 = samples[i];
let p3 = if i + 1 < n {
samples[i + 1]
} else {
samples[n - 1]
};
let t1 = p1
.timestamp
.duration_since(p0.timestamp)
.as_secs_f32()
.max(0.001);
let t2 = p2
.timestamp
.duration_since(p1.timestamp)
.as_secs_f32()
.max(0.001);
let t3 = p3
.timestamp
.duration_since(p2.timestamp)
.as_secs_f32()
.max(0.001);
let target_offset = target_time.duration_since(p1.timestamp).as_secs_f32();
let t = target_offset / t2;
let m1 = Position3D::new(
(p2.position.x - p0.position.x) / (t1 + t2),
(p2.position.y - p0.position.y) / (t1 + t2),
(p2.position.z - p0.position.z) / (t1 + t2),
);
let m2 = Position3D::new(
(p3.position.x - p1.position.x) / (t2 + t3),
(p3.position.y - p1.position.y) / (t2 + t3),
(p3.position.z - p1.position.z) / (t2 + t3),
);
let t2 = t * t;
let t3 = t2 * t;
let h00 = 2.0 * t3 - 3.0 * t2 + 1.0;
let h10 = t3 - 2.0 * t2 + t;
let h01 = -2.0 * t3 + 3.0 * t2;
let h11 = t3 - t2;
return Some(Position3D::new(
h00 * p1.position.x + h10 * m1.x * t2 + h01 * p2.position.x + h11 * m2.x * t2,
h00 * p1.position.y + h10 * m1.y * t2 + h01 * p2.position.y + h11 * m2.y * t2,
h00 * p1.position.z + h10 * m1.z * t2 + h01 * p2.position.z + h11 * m2.z * t2,
));
}
}
self.linear_interpolation(history, target_time)
}
fn kalman_interpolation(
&self,
history: &VecDeque<PositionSnapshot>,
target_time: Instant,
) -> Option<Position3D> {
if history.len() < 2 {
return history.back().map(|s| s.position);
}
let latest = history.back()?;
let time_delta = target_time.duration_since(latest.timestamp).as_secs_f32();
Some(Position3D::new(
latest.position.x + latest.velocity.x * time_delta,
latest.position.y + latest.velocity.y * time_delta,
latest.position.z + latest.velocity.z * time_delta,
))
}
fn physics_interpolation(
&self,
history: &VecDeque<PositionSnapshot>,
target_time: Instant,
) -> Option<Position3D> {
if history.len() < 3 {
return self.kalman_interpolation(history, target_time);
}
let samples: Vec<_> = history.iter().rev().take(3).collect();
let latest = samples[0];
let prev1 = samples[1];
let prev2 = samples[2];
let dt1 = latest
.timestamp
.duration_since(prev1.timestamp)
.as_secs_f32()
.max(0.001);
let dt2 = prev1
.timestamp
.duration_since(prev2.timestamp)
.as_secs_f32()
.max(0.001);
let accel = Position3D::new(
(latest.velocity.x - prev1.velocity.x) / dt1,
(latest.velocity.y - prev1.velocity.y) / dt1,
(latest.velocity.z - prev1.velocity.z) / dt1,
);
let time_delta = target_time.duration_since(latest.timestamp).as_secs_f32();
Some(Position3D::new(
latest.position.x
+ latest.velocity.x * time_delta
+ 0.5 * accel.x * time_delta * time_delta,
latest.position.y
+ latest.velocity.y * time_delta
+ 0.5 * accel.y * time_delta * time_delta,
latest.position.z
+ latest.velocity.z * time_delta
+ 0.5 * accel.z * time_delta * time_delta,
))
}
pub fn estimate_latency(&self, user_id: &UserId) -> Option<f64> {
let history = self.position_histories.get(user_id)?;
if history.len() < 2 {
return Some(0.0);
}
let mut total_intervals = 0.0;
let mut interval_count = 0;
for i in 1..history.len() {
let current_sample = &history[i];
let previous_sample = &history[i - 1];
let interval = current_sample
.timestamp
.duration_since(previous_sample.timestamp)
.as_secs_f64()
* 1000.0;
total_intervals += interval;
interval_count += 1;
}
if interval_count > 0 {
let avg_interval = total_intervals / interval_count as f64;
Some((avg_interval * 0.5).min(100.0)) } else {
Some(0.0)
}
}
}
impl Default for PositionInterpolator {
fn default() -> Self {
Self::new()
}
}
impl LatencyCompensator {
pub fn new() -> Self {
Self {
user_latencies: HashMap::new(),
compensation_method: CompensationMethod::Adaptive,
max_compensation_ms: 100.0,
}
}
pub fn update_user_latency(&mut self, user_id: &UserId, latency_ms: f64) {
self.user_latencies.insert(user_id.clone(), latency_ms);
}
pub fn get_compensation_delay(&self, user_id: &UserId) -> f64 {
if let Some(&latency) = self.user_latencies.get(user_id) {
(latency * 0.5).min(self.max_compensation_ms)
} else {
0.0
}
}
}
impl Default for LatencyCompensator {
fn default() -> Self {
Self::new()
}
}
impl MultiUserAudioProcessor {
pub fn new(config: &MultiUserConfig) -> Result<Self> {
let mixer_config = MixerConfig {
max_sources: config.max_sources_per_user * config.max_users_per_room,
buffer_size: 1024,
sample_rate: 48000,
spatial_quality: config.audio_quality,
optimization_level: OptimizationLevel::Balanced,
};
Ok(Self {
mixer: SpatialAudioMixer::new(mixer_config),
vad: VoiceActivityDetector::new(),
effects: AudioEffectsProcessor::new(),
codec: AudioCodec::new(AudioFormat::Opus),
})
}
pub fn process_for_user(
&mut self,
listener: &MultiUserUser,
all_users: &HashMap<UserId, MultiUserUser>,
sources: &Arc<RwLock<HashMap<SourceId, MultiUserAudioSource>>>,
) -> Result<Vec<f32>> {
self.mixer
.set_listener_position(&listener.id, listener.position);
let sources_guard = sources.read().map_err(|e| {
Error::LegacyProcessing(format!("Failed to acquire read lock on sources: {}", e))
})?;
let mut mixed_audio = vec![0.0f32; 1024];
for (source_id, source) in sources_guard.iter() {
if self.is_source_audible(listener, source)? {
let spatial_audio = self.mixer.process_source(source, listener.position)?;
for (i, sample) in spatial_audio.iter().enumerate() {
if i < mixed_audio.len() {
mixed_audio[i] += sample;
}
}
}
}
self.effects
.process_user_effects(&listener.id, &mut mixed_audio)?;
Ok(mixed_audio)
}
fn is_source_audible(
&self,
listener: &MultiUserUser,
source: &MultiUserAudioSource,
) -> Result<bool> {
let distance = listener.position.distance_to(&source.position);
if distance > source.spatial_properties.max_distance {
return Ok(false);
}
match source.access_control.visibility {
SourceVisibility::Public => Ok(true),
SourceVisibility::Private => Ok(source.owner_id == listener.id),
SourceVisibility::Whitelist => {
Ok(source.access_control.allowed_users.contains(&listener.id))
}
SourceVisibility::Friends => {
Ok(listener.friends.contains(&source.owner_id) || source.owner_id == listener.id)
}
SourceVisibility::Moderators => Ok(matches!(
listener.role,
UserRole::Moderator | UserRole::Administrator
)),
}
}
}
impl SpatialAudioMixer {
pub fn new(config: MixerConfig) -> Self {
Self {
listener_positions: HashMap::new(),
source_manager: Arc::new(RwLock::new(HashMap::new())),
mixer_config: config,
}
}
pub fn set_listener_position(&mut self, user_id: &UserId, position: Position3D) {
self.listener_positions.insert(user_id.clone(), position);
}
pub fn process_source(
&self,
source: &MultiUserAudioSource,
listener_position: Position3D,
) -> Result<Vec<f32>> {
let distance = listener_position.distance_to(&source.position);
let attenuation = self.calculate_distance_attenuation(distance, &source.spatial_properties);
let buffer_size = self.mixer_config.buffer_size;
let mut output = vec![0.0f32; buffer_size];
let volume = source.volume * attenuation;
for sample in output.iter_mut() {
*sample = volume * 0.1; }
Ok(output)
}
fn calculate_distance_attenuation(&self, distance: f32, properties: &SpatialProperties) -> f32 {
if distance <= properties.reference_distance {
return 1.0;
}
let ratio = properties.reference_distance / distance;
ratio.powf(properties.rolloff_factor).min(1.0)
}
}
impl VoiceActivityDetector {
pub fn new() -> Self {
Self {
algorithm: VadAlgorithm::Energy,
user_states: HashMap::new(),
thresholds: VadThresholds {
energy_threshold: 0.01,
min_speaking_duration_ms: 100,
min_silence_duration_ms: 200,
confidence_threshold: 0.7,
},
}
}
pub fn process_user_audio(&mut self, user_id: &UserId, audio_buffer: &[f32]) -> bool {
let energy = audio_buffer.iter().map(|&x| x * x).sum::<f32>() / audio_buffer.len() as f32;
let state = self
.user_states
.entry(user_id.clone())
.or_insert_with(|| VadState {
is_speaking: false,
confidence: 0.0,
energy_history: VecDeque::new(),
speaking_duration: Duration::new(0, 0),
silence_duration: Duration::new(0, 0),
});
state.energy_history.push_back(energy);
if state.energy_history.len() > 10 {
state.energy_history.pop_front();
}
state.is_speaking = energy > self.thresholds.energy_threshold;
state.confidence = if state.is_speaking { 0.9 } else { 0.1 };
state.is_speaking
}
}
impl Default for VoiceActivityDetector {
fn default() -> Self {
Self::new()
}
}
impl AudioEffectsProcessor {
pub fn new() -> Self {
Self {
effects: HashMap::new(),
user_effects: HashMap::new(),
zone_effects: HashMap::new(),
}
}
pub fn process_user_effects(
&mut self,
user_id: &UserId,
audio_buffer: &mut [f32],
) -> Result<()> {
if let Some(effect_chain) = self.user_effects.get(user_id) {
let mut temp_buffer = audio_buffer.to_vec();
for effect_name in effect_chain {
if let Some(effect) = self.effects.get_mut(effect_name) {
effect.process(&temp_buffer, audio_buffer)?;
temp_buffer.copy_from_slice(audio_buffer);
}
}
}
Ok(())
}
}
impl Default for AudioEffectsProcessor {
fn default() -> Self {
Self::new()
}
}
impl AudioCodec {
pub fn new(format: AudioFormat) -> Self {
Self {
format,
compression: CompressionSettings {
bitrate_kbps: 64,
complexity: 5,
variable_bitrate: true,
low_latency: true,
},
codec_states: HashMap::new(),
}
}
}
pub struct MultiUserConfigBuilder {
config: MultiUserConfig,
}
impl MultiUserConfigBuilder {
pub fn new() -> Self {
Self {
config: MultiUserConfig::default(),
}
}
pub fn max_users(mut self, max_users: usize) -> Self {
self.config.max_users_per_room = max_users;
self
}
pub fn audio_quality(mut self, quality: f32) -> Self {
self.config.audio_quality = quality.clamp(0.0, 1.0);
self
}
pub fn max_latency_ms(mut self, latency: f64) -> Self {
self.config.max_latency_ms = latency;
self
}
pub fn enable_encryption(mut self, enabled: bool) -> Self {
self.config.privacy_settings.encryption_enabled = enabled;
self
}
pub fn bandwidth_limit_kbps(mut self, limit: u32) -> Self {
self.config.bandwidth_settings.max_bandwidth_kbps = limit;
self
}
pub fn build(self) -> MultiUserConfig {
self.config
}
}
impl Default for MultiUserConfigBuilder {
fn default() -> Self {
Self::new()
}
}