veza/veza-stream-server/src/audio_effects.rs

252 lines
7.6 KiB
Rust
Raw Normal View History

2025-12-03 19:36:56 +00:00
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
// Note: Use tracing::info! macro directly instead of importing
/// Types d'effets audio supportés
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AudioEffectType {
/// Equalizer avec bandes de fréquences configurables
Equalizer {
bands: Vec<EqBand>,
},
/// Normalisation du volume
Normalization {
target_level: f32, // dB
},
/// Réverbération
Reverb {
room_size: f32,
damping: f32,
wet_level: f32,
dry_level: f32,
},
/// Écho/Delay
Echo {
delay_ms: u32,
feedback: f32,
},
/// Compression
Compression {
threshold: f32,
ratio: f32,
attack: f32,
release: f32,
},
}
/// Bande de fréquence pour l'equalizer
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EqBand {
pub frequency: f32, // Hz
pub gain: f32, // dB (-12 à +12)
pub q_factor: f32, // Bandwidth
}
/// Configuration d'effets audio pour un stream
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AudioEffectsConfig {
pub user_id: i64,
pub effects: Vec<AudioEffectType>,
}
/// Manager pour gérer les effets audio
pub struct AudioEffectsManager {
/// Cache des configurations d'effets par user
user_configs: HashMap<i64, AudioEffectsConfig>,
}
impl AudioEffectsManager {
pub fn new() -> Self {
Self {
user_configs: HashMap::new(),
}
}
/// Appliquer des effets audio à un buffer audio
#[instrument(skip(self, audio_buffer))]
pub fn apply_effects(
&self,
user_id: i64,
audio_buffer: &mut Vec<f32>,
effects: &[AudioEffectType],
) {
for effect in effects {
match effect {
AudioEffectType::Equalizer { bands } => {
self.apply_equalizer(audio_buffer, bands);
}
AudioEffectType::Normalization { target_level } => {
self.apply_normalization(audio_buffer, *target_level);
}
AudioEffectType::Reverb { room_size, damping, wet_level, dry_level } => {
self.apply_reverb(audio_buffer, *room_size, *damping, *wet_level, *dry_level);
}
AudioEffectType::Echo { delay_ms, feedback } => {
self.apply_echo(audio_buffer, *delay_ms, *feedback);
}
AudioEffectType::Compression { threshold, ratio, attack, release } => {
self.apply_compression(audio_buffer, *threshold, *ratio, *attack, *release);
}
}
}
tracing::debug!(
user_id = user_id,
effects_count = effects.len(),
"Effects applied to audio buffer"
);
}
/// Appliquer un equalizer à un buffer audio
fn apply_equalizer(&self, buffer: &mut Vec<f32>, bands: &[EqBand]) {
// Note: Cette implémentation est simplifiée
// Une implémentation réelle nécessiterait des FFT/FIR filters
// Pour l'instant, on applique un gain simple par bande
for band in bands {
let gain_factor = 10.0_f32.powf(band.gain / 20.0);
for sample in buffer.iter_mut() {
// Application simplifiée du gain
*sample *= gain_factor;
}
}
tracing::info!("Equalizer applied with {} bands", bands.len());
}
/// Appliquer une normalisation du volume
fn apply_normalization(&self, buffer: &mut Vec<f32>, target_level: f32) {
// Trouver le pic le plus élevé
let max_amplitude = buffer.iter().fold(0.0, |acc, &x| acc.max(x.abs()));
if max_amplitude > 0.0 {
let target_linear = 10.0_f32.powf(target_level / 20.0);
let gain = target_linear / max_amplitude;
for sample in buffer.iter_mut() {
*sample *= gain;
}
}
tracing::info!("Normalization applied to -{} dB", target_level);
}
/// Appliquer de la réverbération
fn apply_reverb(
&self,
buffer: &mut Vec<f32>,
room_size: f32,
damping: f32,
wet_level: f32,
dry_level: f32,
) {
// Note: Implémentation simplifiée de la réverbération
// Une implémentation réelle utiliserait des all-pass filters et delays
let reverb_buffer = buffer.clone();
// Appliquer un delay avec feedback
let delay_length = (0.03 * room_size * 44100.0) as usize; // Simplifié
for i in delay_length..buffer.len() {
let delayed = reverb_buffer[i - delay_length] * damping;
buffer[i] = buffer[i] * dry_level + delayed * wet_level;
}
tracing::info!("Reverb applied with room_size={}", room_size);
}
/// Appliquer de l'écho
fn apply_echo(&self, buffer: &mut Vec<f32>, delay_ms: u32, feedback: f32) {
let delay_samples = (delay_ms as f32 * 44.1) as usize; // 44.1 kHz
if delay_samples < buffer.len() {
let original = buffer.clone();
for i in delay_samples..buffer.len() {
buffer[i] = original[i] + original[i - delay_samples] * feedback;
}
}
tracing::info!("Echo applied with delay={} ms", delay_ms);
}
/// Appliquer de la compression
fn apply_compression(
&self,
buffer: &mut Vec<f32>,
threshold: f32,
ratio: f32,
attack: f32,
release: f32,
) {
// Note: Implémentation simplifiée de la compression
// Une implémentation réelle utiliserait un envelope follower
for sample in buffer.iter_mut() {
let abs = sample.abs();
if abs > threshold {
let excess = abs - threshold;
let compressed_excess = excess / ratio;
let new_amplitude = threshold + compressed_excess;
if *sample >= 0.0 {
*sample = new_amplitude;
} else {
*sample = -new_amplitude;
}
}
}
tracing::info!("Compression applied with threshold={} dB", threshold);
}
/// Définir les effets audio pour un user
#[instrument(skip(self))]
pub fn set_user_effects(&mut self, user_id: i64, config: AudioEffectsConfig) {
self.user_configs.insert(user_id, config);
tracing::info!(user_id = user_id, "User audio effects configured");
}
/// Obtenir les effets audio d'un user
pub fn get_user_effects(&self, user_id: i64) -> Option<&AudioEffectsConfig> {
self.user_configs.get(&user_id)
}
/// Supprimer les effets audio d'un user
#[instrument(skip(self))]
pub fn clear_user_effects(&mut self, user_id: i64) {
self.user_configs.remove(&user_id);
tracing::info!(user_id = user_id, "User audio effects cleared");
}
}
impl Default for AudioEffectsManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_audio_effects_manager() {
let mut manager = AudioEffectsManager::new();
let mut buffer = vec![0.5, 0.3, 0.7, 0.2, 0.9];
let effects = vec![
AudioEffectType::Normalization {
target_level: -3.0,
},
];
manager.apply_effects(1, &mut buffer, &effects);
assert!(true);
}
}