amélioration de la webapp
This commit is contained in:
@@ -90,7 +90,13 @@ impl AudioChunk {
|
||||
}
|
||||
|
||||
/// Crée un nouveau chunk audio avec un gain spécifique
|
||||
pub fn with_gain(order: u64, left: Vec<f32>, right: Vec<f32>, sample_rate: u32, gain: f32) -> Self {
|
||||
pub fn with_gain(
|
||||
order: u64,
|
||||
left: Vec<f32>,
|
||||
right: Vec<f32>,
|
||||
sample_rate: u32,
|
||||
gain: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
order,
|
||||
left: Arc::new(left),
|
||||
|
||||
@@ -76,13 +76,13 @@
|
||||
//! - **RwLock** : Pour partage concurrent du compteur [`TimerNode`]
|
||||
|
||||
mod audio_chunk;
|
||||
mod nodes;
|
||||
pub mod events;
|
||||
mod nodes;
|
||||
|
||||
pub use audio_chunk::AudioChunk;
|
||||
pub use events::{
|
||||
AudioDataEvent, EventPublisher, EventReceiver, NodeEvent, NodeListener,
|
||||
SourceNameUpdateEvent, VolumeChangeEvent,
|
||||
AudioDataEvent, EventPublisher, EventReceiver, NodeEvent, NodeListener, SourceNameUpdateEvent,
|
||||
VolumeChangeEvent,
|
||||
};
|
||||
pub use nodes::{
|
||||
buffer_node::BufferNode,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::{AudioChunk, nodes::{AudioError, MultiSubscriberNode}};
|
||||
use crate::{
|
||||
nodes::{AudioError, MultiSubscriberNode},
|
||||
AudioChunk,
|
||||
};
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{mpsc, RwLock};
|
||||
|
||||
@@ -189,10 +189,7 @@ impl ChromecastSink {
|
||||
// Établir la connexion
|
||||
self.connect().await?;
|
||||
|
||||
let mut stats = ChromecastStats::new(
|
||||
self.node_id.clone(),
|
||||
self.config.device_name.clone(),
|
||||
);
|
||||
let mut stats = ChromecastStats::new(self.node_id.clone(), self.config.device_name.clone());
|
||||
|
||||
// Boucle principale
|
||||
while let Some(chunk) = self.rx.recv().await {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::{AudioChunk, nodes::{AudioError, MultiSubscriberNode}};
|
||||
use crate::{
|
||||
nodes::{AudioError, MultiSubscriberNode},
|
||||
AudioChunk,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@@ -56,10 +59,10 @@ impl DecoderNode {
|
||||
|
||||
if src_idx < left_data.len() - 1 {
|
||||
let frac = src_pos - src_idx as f64;
|
||||
let left_sample =
|
||||
left_data[src_idx] * (1.0 - frac as f32) + left_data[src_idx + 1] * frac as f32;
|
||||
let right_sample =
|
||||
right_data[src_idx] * (1.0 - frac as f32) + right_data[src_idx + 1] * frac as f32;
|
||||
let left_sample = left_data[src_idx] * (1.0 - frac as f32)
|
||||
+ left_data[src_idx + 1] * frac as f32;
|
||||
let right_sample = right_data[src_idx] * (1.0 - frac as f32)
|
||||
+ right_data[src_idx + 1] * frac as f32;
|
||||
|
||||
new_left.push(left_sample);
|
||||
new_right.push(right_sample);
|
||||
@@ -69,7 +72,8 @@ impl DecoderNode {
|
||||
}
|
||||
}
|
||||
|
||||
let new_chunk = AudioChunk::new(chunk.order, new_left, new_right, target_sample_rate);
|
||||
let new_chunk =
|
||||
AudioChunk::new(chunk.order, new_left, new_right, target_sample_rate);
|
||||
self.subscribers.push(Arc::new(new_chunk)).await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
//! Ce module fournit un sink qui écrit les chunks audio sur disque,
|
||||
//! avec support de la dérivation automatique du nom de fichier depuis la source.
|
||||
|
||||
use crate::{
|
||||
events::SourceNameUpdateEvent,
|
||||
nodes::AudioError,
|
||||
AudioChunk,
|
||||
};
|
||||
use crate::{events::SourceNameUpdateEvent, nodes::AudioError, AudioChunk};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::fs::File;
|
||||
@@ -161,7 +157,13 @@ impl DiskSink {
|
||||
// Nettoyer le nom de la source pour en faire un nom de fichier valide
|
||||
let clean_name = name
|
||||
.chars()
|
||||
.map(|c| if c.is_alphanumeric() || c == '_' || c == '-' { c } else { '_' })
|
||||
.map(|c| {
|
||||
if c.is_alphanumeric() || c == '_' || c == '-' {
|
||||
c
|
||||
} else {
|
||||
'_'
|
||||
}
|
||||
})
|
||||
.collect::<String>();
|
||||
|
||||
format!("{}.{}", clean_name, self.config.format.extension())
|
||||
@@ -180,9 +182,9 @@ impl DiskSink {
|
||||
|
||||
// Créer le répertoire parent si nécessaire
|
||||
if let Some(parent) = path.parent() {
|
||||
tokio::fs::create_dir_all(parent)
|
||||
.await
|
||||
.map_err(|e| AudioError::ProcessingError(format!("Failed to create directory: {}", e)))?;
|
||||
tokio::fs::create_dir_all(parent).await.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to create directory: {}", e))
|
||||
})?;
|
||||
}
|
||||
|
||||
// Créer le writer approprié selon le format
|
||||
@@ -328,10 +330,9 @@ impl AudioFileWriter {
|
||||
bytes.extend_from_slice(&sample_i16.to_le_bytes());
|
||||
}
|
||||
|
||||
self.file
|
||||
.write_all(&bytes)
|
||||
.await
|
||||
.map_err(|e| AudioError::ProcessingError(format!("Failed to write audio data: {}", e)))?;
|
||||
self.file.write_all(&bytes).await.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to write audio data: {}", e))
|
||||
})?;
|
||||
|
||||
self.total_samples += chunk.len();
|
||||
Ok(())
|
||||
@@ -361,10 +362,9 @@ impl AudioFileWriter {
|
||||
header.extend_from_slice(b"data");
|
||||
header.extend_from_slice(&0u32.to_le_bytes()); // Taille des données (à mettre à jour)
|
||||
|
||||
self.file
|
||||
.write_all(&header)
|
||||
.await
|
||||
.map_err(|e| AudioError::ProcessingError(format!("Failed to write WAV header: {}", e)))?;
|
||||
self.file.write_all(&header).await.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to write WAV header: {}", e))
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -378,24 +378,37 @@ impl AudioFileWriter {
|
||||
|
||||
// Positionner au début et réécrire les tailles
|
||||
use tokio::io::AsyncSeekExt;
|
||||
self.file.seek(std::io::SeekFrom::Start(4)).await.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to seek in file: {}", e))
|
||||
})?;
|
||||
self.file.write_all(&file_size.to_le_bytes()).await.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to update file size: {}", e))
|
||||
})?;
|
||||
self.file
|
||||
.seek(std::io::SeekFrom::Start(4))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to seek in file: {}", e))
|
||||
})?;
|
||||
self.file
|
||||
.write_all(&file_size.to_le_bytes())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to update file size: {}", e))
|
||||
})?;
|
||||
|
||||
self.file.seek(std::io::SeekFrom::Start(40)).await.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to seek in file: {}", e))
|
||||
})?;
|
||||
self.file.write_all(&data_size.to_le_bytes()).await.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to update data size: {}", e))
|
||||
})?;
|
||||
self.file
|
||||
.seek(std::io::SeekFrom::Start(40))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to seek in file: {}", e))
|
||||
})?;
|
||||
self.file
|
||||
.write_all(&data_size.to_le_bytes())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to update data size: {}", e))
|
||||
})?;
|
||||
}
|
||||
|
||||
self.file.flush().await.map_err(|e| {
|
||||
AudioError::ProcessingError(format!("Failed to flush file: {}", e))
|
||||
})?;
|
||||
self.file
|
||||
.flush()
|
||||
.await
|
||||
.map_err(|e| AudioError::ProcessingError(format!("Failed to flush file: {}", e)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::{AudioChunk, nodes::{AudioError, MultiSubscriberNode}};
|
||||
use crate::{
|
||||
nodes::{AudioError, MultiSubscriberNode},
|
||||
AudioChunk,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@@ -46,12 +49,8 @@ impl DspNode {
|
||||
*sample *= self.gain;
|
||||
}
|
||||
|
||||
let new_chunk = AudioChunk::new(
|
||||
chunk.order,
|
||||
left_data,
|
||||
right_data,
|
||||
chunk.sample_rate,
|
||||
);
|
||||
let new_chunk =
|
||||
AudioChunk::new(chunk.order, left_data, right_data, chunk.sample_rate);
|
||||
|
||||
self.subscribers.push(Arc::new(new_chunk)).await?;
|
||||
}
|
||||
@@ -118,12 +117,7 @@ impl LowPassDspNode {
|
||||
new_right.push(self.prev_right);
|
||||
}
|
||||
|
||||
let new_chunk = AudioChunk::new(
|
||||
chunk.order,
|
||||
new_left,
|
||||
new_right,
|
||||
chunk.sample_rate,
|
||||
);
|
||||
let new_chunk = AudioChunk::new(chunk.order, new_left, new_right, chunk.sample_rate);
|
||||
|
||||
self.subscribers.push(Arc::new(new_chunk)).await?;
|
||||
}
|
||||
|
||||
@@ -59,10 +59,7 @@ impl SingleSubscriberNode {
|
||||
}
|
||||
|
||||
pub async fn push(&self, chunk: Arc<AudioChunk>) -> Result<(), AudioError> {
|
||||
self.tx
|
||||
.send(chunk)
|
||||
.await
|
||||
.map_err(|_| AudioError::SendError)
|
||||
self.tx.send(chunk).await.map_err(|_| AudioError::SendError)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -197,7 +197,9 @@ impl MpdSink {
|
||||
/// Envoie un chunk au serveur MPD (mock)
|
||||
async fn send_chunk(&self, _chunk: &AudioChunk) -> Result<(), AudioError> {
|
||||
if !self.connected {
|
||||
return Err(AudioError::ProcessingError("Not connected to MPD".to_string()));
|
||||
return Err(AudioError::ProcessingError(
|
||||
"Not connected to MPD".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// Dans une vraie implémentation:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{AudioChunk, nodes::AudioError};
|
||||
use crate::{nodes::AudioError, AudioChunk};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@@ -115,11 +115,13 @@ impl SinkStats {
|
||||
let sum_squares_left: f64 = chunk.left.iter().map(|&x| (x * x) as f64).sum();
|
||||
let sum_squares_right: f64 = chunk.right.iter().map(|&x| (x * x) as f64).sum();
|
||||
|
||||
self.rms_left = ((self.rms_left.powi(2) * (self.total_samples - chunk.len() as u64) as f64
|
||||
self.rms_left = ((self.rms_left.powi(2)
|
||||
* (self.total_samples - chunk.len() as u64) as f64
|
||||
+ sum_squares_left)
|
||||
/ self.total_samples as f64)
|
||||
.sqrt();
|
||||
self.rms_right = ((self.rms_right.powi(2) * (self.total_samples - chunk.len() as u64) as f64
|
||||
self.rms_right = ((self.rms_right.powi(2)
|
||||
* (self.total_samples - chunk.len() as u64) as f64
|
||||
+ sum_squares_right)
|
||||
/ self.total_samples as f64)
|
||||
.sqrt();
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::{AudioChunk, nodes::{AudioError, MultiSubscriberNode}};
|
||||
use crate::{
|
||||
nodes::{AudioError, MultiSubscriberNode},
|
||||
AudioChunk,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@@ -63,12 +66,8 @@ impl SourceNode {
|
||||
sample_rate: u32,
|
||||
) -> Result<(), AudioError> {
|
||||
for i in 0..count {
|
||||
let chunk = AudioChunk::new(
|
||||
i,
|
||||
vec![0.0; chunk_size],
|
||||
vec![0.0; chunk_size],
|
||||
sample_rate,
|
||||
);
|
||||
let chunk =
|
||||
AudioChunk::new(i, vec![0.0; chunk_size], vec![0.0; chunk_size], sample_rate);
|
||||
self.subscribers.push(Arc::new(chunk)).await?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::{AudioChunk, nodes::{AudioError, MultiSubscriberNode}};
|
||||
use crate::{
|
||||
nodes::{AudioError, MultiSubscriberNode},
|
||||
AudioChunk,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{mpsc, RwLock};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user