Files
pmomusic/pmoaudio/src/lib.rs

148 lines
4.3 KiB
Rust
Raw Normal View History

#![cfg_attr(feature = "simd", feature(portable_simd))]
#![doc = r#"
PMOAudio - Pipeline audio stéréo async optimisé
Cette crate fournit un pipeline audio push-based async utilisant Tokio,
optimisé pour minimiser les clonages de données via `Arc<[[i32; 2]]>`.
# Architecture
Le pipeline est composé de nodes asynchrones qui communiquent via des channels Tokio.
Les données audio sont encapsulées dans des [`AudioChunk`] et partagées via `Arc` pour
éviter les copies inutiles.
## Pipeline type
```text
SourceNode DecoderNode DSPNode BufferNode TimerNode SinkNode(s)
Multiroom Sinks
(avec offsets)
```
# Exemples
## Pipeline simple
```no_run
use pmoaudio::{SinkNode, SourceNode, TimerNode};
#[tokio::main]
async fn main() {
let (mut timer, timer_tx) = TimerNode::new(10);
let (sink, sink_tx) = SinkNode::new("Output".to_string(), 10);
timer.add_subscriber(sink_tx);
tokio::spawn(async move { timer.run().await.unwrap() });
let sink_handle = tokio::spawn(async move {
sink.run_with_stats().await.unwrap()
});
tokio::spawn(async move {
let mut source = SourceNode::new();
source.add_subscriber(timer_tx);
source.generate_chunks(30, 4800, 48000, 440.0).await.unwrap();
});
sink_handle.await.unwrap();
}
```
## Configuration multiroom
```no_run
use pmoaudio::{BufferNode, SinkNode};
#[tokio::main]
async fn main() {
let (buffer, buffer_tx) = BufferNode::new(50, 10);
let (sink1, sink1_tx) = SinkNode::new("Room 1".to_string(), 10);
let (sink2, sink2_tx) = SinkNode::new("Room 2".to_string(), 10);
// Room 1 sans délai, Room 2 avec 5 chunks de retard
buffer.add_subscriber_with_offset(sink1_tx, 0).await;
buffer.add_subscriber_with_offset(sink2_tx, 5).await;
tokio::spawn(async move { buffer.run().await.unwrap() });
// ... spawn sinks et source
}
```
# Optimisations
- **Zero-copy** : Les [`AudioChunk`] sont partagés via `Arc`, seul le pointeur est cloné
- **Copy-on-Write** : Les nodes DSP clonent les données uniquement si modification nécessaire
- **Backpressure** : Channels bounded avec `try_send` pour éviter les blocages
- **RwLock** : Pour partage concurrent du compteur [`TimerNode`]
"#]
#[cfg(feature = "simd")]
// use std::simd::*; // Not actually used in this file, modules import their own simd
2025-10-11 00:33:13 +02:00
mod audio_chunk;
mod audio_segment;
pub mod conversions;
2025-11-01 21:10:57 +01:00
pub mod events;
pub mod nodes;
pub mod pipeline;
2025-11-01 21:10:57 +01:00
mod sample_types;
mod sync_marker;
pub mod type_constraints;
#[macro_use]
mod macros;
2025-10-11 00:33:13 +02:00
pub mod bit_depth;
pub mod dsp;
pub use audio_segment::{AudioSegment, _AudioSegment};
2025-11-01 21:10:57 +01:00
pub use sync_marker::SyncMarker;
2025-11-01 21:10:57 +01:00
pub use audio_chunk::{
gain_db_from_linear, gain_linear_from_db, AudioChunk, AudioChunkData, AudioFloatChunk,
AudioIntegerChunk,
};
pub use bit_depth::{Bit16, Bit24, Bit32, Bit8, BitDepth};
2025-11-01 21:10:57 +01:00
pub use sample_types::{Sample, I24};
pub use type_constraints::{
check_compatibility, SampleType, TypeCategory, TypeMismatch, TypeRequirement,
};
2025-10-11 15:36:06 +02:00
pub use events::{
2025-10-19 13:42:29 +02:00
AudioDataEvent, EventPublisher, EventReceiver, NodeEvent, NodeListener, SourceNameUpdateEvent,
VolumeChangeEvent,
2025-10-11 15:36:06 +02:00
};
// Export du trait de pipeline
pub use pipeline::AudioPipelineNode;
2025-11-01 21:10:57 +01:00
// Exports publics des nodes
pub use nodes::{
audio_sink::AudioSink,
2025-11-01 21:10:57 +01:00
converter_nodes::{ToF32Node, ToF64Node, ToI16Node, ToI24Node, ToI32Node},
file_source::FileSource,
flac_file_sink::{FlacFileSink, FlacFileSinkStats},
http_source::HttpSource,
feat: Add PlaylistSource and ResamplingNode for playlist playback This commit implements a new audio source that reads from pmoplaylist and streams tracks continuously, along with a resampling node to normalize sample rates. ## New Components ### PlaylistSource (pmoaudio-ext) - New source in pmoaudio-ext/src/sources/playlist_source.rs - Reads from pmoplaylist ReadHandle - Decodes tracks from audio cache (pmoaudiocache) - Emits PCM with heterogeneous sample_rate and bit_depth - Polls playlist when empty (configurable interval, default 100ms) - Emits TrackBoundary markers between tracks - Graceful shutdown with EndOfStream on stop - Gated behind 'playlist' feature flag **Design Philosophy:** - Keeps each node simple (single responsibility) - Emits raw PCM without format normalization - Pipeline designer chooses how to handle heterogeneity - Ideal for Radio Paradise (homogeneous streams) - Requires ResamplingNode + ToI24Node for mixed playlists ### ResamplingNode (pmoaudio) - Generic resampling node in pmoaudio/src/nodes/resampling_node.rs - Normalizes variable sample rates to a target rate - Uses libsoxr for high-quality resampling - Automatically detects sample rate changes - Recreates resampler as needed - Preserves chunk type (I16/I24/I32/F32/F64) - Quality adapts to bit depth (Medium/High/Very High) ## Architecture PlaylistSource is placed in pmoaudio-ext to avoid circular dependencies: - pmoaudio-ext depends on: pmoaudio, pmoplaylist, pmoaudiocache - No reverse dependencies = clean dependency graph ## Configuration ### pmoaudio-ext/Cargo.toml - Updated 'playlist' feature to include pmoaudiocache, pmocache, pmoflac - Added sources module export ### pmoaudio - Added resampling_node module - Public export: ResamplingNode ## System Requirements ⚠️ **IMPORTANT**: libsoxr-dev must be installed for compilation See INSTALL_NOTES.md for installation instructions per platform. ## Usage Example ```rust // Radio Paradise (homogeneous 44.1kHz/16bit) let mut source = PlaylistSource::new(playlist, cache); let to_i24 = ToI24Node::new(); source.register(Box::new(to_i24)); // Mixed playlist (needs normalization) let mut source = PlaylistSource::new(playlist, cache); let mut resampler = ResamplingNode::new(48000); // Force 48kHz let to_i24 = ToI24Node::new(); source.register(Box::new(resampler)); resampler.register(Box::new(to_i24)); ``` ## Files Changed - pmoaudio-ext/Cargo.toml: Update playlist feature - pmoaudio-ext/src/lib.rs: Add sources module - pmoaudio-ext/src/sources/mod.rs: New sources module - pmoaudio-ext/src/sources/playlist_source.rs: New PlaylistSource (580 lines) - pmoaudio/src/nodes/resampling_node.rs: New ResamplingNode (350 lines) - pmoaudio/src/nodes/mod.rs: Register resampling_node - pmoaudio/src/lib.rs: Export ResamplingNode - INSTALL_NOTES.md: System requirements documentation ## Future Work - GapInsertionNode (inserts silence between tracks) - CrossfadeNode (fade-in/fade-out mixing) - Examples (deferred until implementation validated)
2025-11-05 13:44:24 +00:00
resampling_node::ResamplingNode,
2025-11-14 10:43:53 +01:00
timer_buffer_node::TimerBufferNode,
timer_node::TimerNode,
position_tracker_node::{PositionHandle, PositionTrackerNode},
AudioError, AudioNode, TypedAudioNode,
2025-11-01 21:10:57 +01:00
};
// Nodes temporairement désactivés
/*
2025-10-11 00:33:13 +02:00
pub use nodes::{
buffer_node::BufferNode,
2025-10-11 15:36:06 +02:00
chromecast_sink::{ChromecastConfig, ChromecastSink, ChromecastStats, StreamEncoding},
2025-10-11 00:33:13 +02:00
decoder_node::DecoderNode,
2025-10-11 15:36:06 +02:00
disk_sink::{AudioFileFormat, DiskSink, DiskSinkConfig, DiskSinkStats},
2025-10-11 00:33:13 +02:00
dsp_node::DspNode,
2025-10-11 15:36:06 +02:00
mpd_sink::{MpdAudioFormat, MpdConfig, MpdHandle, MpdSink, MpdStats},
2025-10-11 00:33:13 +02:00
sink_node::{SinkNode, SinkStats},
source_node::SourceNode,
timer_node::{TimerHandle, TimerNode},
2025-10-11 15:36:06 +02:00
volume_node::{HardwareVolumeNode, VolumeHandle, VolumeNode},
2025-10-11 00:33:13 +02:00
};
*/