Files
pmomusic/pmoaudio-ext/src/sources/playlist_source.rs

1041 lines
39 KiB
Rust
Raw Normal View History

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
//! PlaylistSource - Source audio depuis une playlist pmoplaylist
//!
//! Cette source lit une playlist (via `ReadHandle`) et émet un flux audio
//! continu en décodant les fichiers depuis le cache audio.
//!
//! # ⚠️ Format de sortie hétérogène
//!
//! **IMPORTANT** : Cette source émet du PCM avec des caractéristiques
//! **variables** selon les fichiers sources :
//! - **Sample rate** : peut varier (44.1kHz, 48kHz, 96kHz, etc.)
//! - **Bit depth** : peut varier (I16, I24, I32)
//!
//! Pour obtenir un flux **homogène**, ajoutez les nœuds suivants dans le pipeline :
//! - `ResamplingNode` : normalise le sample_rate (à implémenter dans pmoaudio)
//! - `ToI24Node` / `ToI16Node` : normalise la profondeur de bits
//!
//! # Cas d'usage
//!
//! ## Radio Paradise (format homogène connu)
//! ```rust,no_run
//! use pmoaudio_ext::PlaylistSource;
//! use pmoaudio::ToI24Node;
//! use pmoplaylist::PlaylistManager;
//! use pmoaudiocache::AudioCache;
//! use std::sync::Arc;
//!
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let manager = PlaylistManager::get();
//! let read_handle = manager.get_read_handle("radio-paradise").await?;
//! let cache = Arc::new(AudioCache::new("./cache", 500)?);
//!
//! let mut source = PlaylistSource::new(read_handle, cache);
//! let to_i24 = ToI24Node::new();
//! source.register(to_i24);
//! # Ok(())
//! # }
//! ```
//!
//! ## Playlist mixte (nécessite homogénéisation)
//! ```rust,no_run
//! use pmoaudio_ext::PlaylistSource;
//! use pmoaudio::{ToI24Node, ResamplingNode};
//! # use pmoplaylist::PlaylistManager;
//! # use pmoaudiocache::AudioCache;
//! # use std::sync::Arc;
//!
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! # let manager = PlaylistManager::get();
//! # let read_handle = manager.get_read_handle("mixed").await?;
//! # let cache = Arc::new(AudioCache::new("./cache", 500)?);
//! let mut source = PlaylistSource::new(read_handle, cache);
//! let mut resampler = ResamplingNode::new(48000); // Force 48kHz
//! let to_i24 = ToI24Node::new(); // Force I24
//! source.register(Box::new(resampler));
//! resampler.register(Box::new(to_i24));
//! # Ok(())
//! # }
//! ```
//!
//! # Historique des morceaux joués
//!
//! Utilisez `PlaylistSource::with_history()` pour créer une source qui transfère
//! automatiquement les morceaux joués vers une playlist historique :
//!
//! ```rust,no_run
//! use pmoaudio_ext::PlaylistSource;
//! use pmoplaylist::PlaylistManager;
//! use pmoaudiocache::cache::new_cache;
//! use std::sync::Arc;
//!
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let manager = PlaylistManager::get();
//! let cache = Arc::new(new_cache("./cache", 500)?);
//!
//! // Playlist live (consommée par la source)
//! let live_read = manager.get_read_handle("radio-live").await?;
//!
//! // Playlist historique (capacité 200 morceaux)
//! let history_write = manager.create_persistent_playlist("radio-history".into()).await?;
//! history_write.set_capacity(Some(200)).await?;
//!
//! // Créer la source avec historique
//! let source = PlaylistSource::with_history(
//! live_read,
//! cache,
//! Arc::new(history_write)
//! );
//!
//! // Les morceaux joués seront automatiquement ajoutés à "radio-history"
//! # Ok(())
//! # }
//! ```
//!
//! **Note** : L'historique utilise `push()` sans TTL. Les morceaux restent dans l'historique
//! jusqu'à ce que la capacité maximale soit atteinte (FIFO).
//!
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
//! # Comportement
//!
//! - **Polling** : Si la playlist est vide, attend `poll_interval_ms` avant de réessayer
//! - **TrackBoundary** : Émet un marqueur avec metadata entre chaque piste
//! - **Erreurs** : Si un fichier est inaccessible, émet un `Error` marker et continue
//! - **Arrêt** : Via `CancellationToken`, émet `EndOfStream` avant de terminer
//! - **Historique** : Si configuré, ajoute chaque piste jouée à la playlist historique
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
//!
//! # Synchronisation
//!
//! - `TopZeroSync` : émis une seule fois au début
//! - `TrackBoundary` : émis avant chaque nouvelle piste (contient metadata)
//! - Pas d'`EndOfStream` entre les pistes (flux continu)
//! - `EndOfStream` final uniquement lors de l'arrêt
use pmoaudio::{
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
nodes::{AudioError, TypedAudioNode, DEFAULT_CHUNK_DURATION_MS},
pipeline::{send_to_children, AudioPipelineNode, Node, NodeLogic},
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
type_constraints::TypeRequirement,
AudioChunk, AudioChunkData, AudioSegment, I24,
};
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
use pmoaudiocache::Cache as AudioCache;
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
use pmoflac::{decode_audio_stream, StreamInfo};
use pmoplaylist::ReadHandle;
use std::{path::PathBuf, sync::Arc, time::Duration};
use tokio::{fs::File, io::AsyncReadExt, sync::mpsc};
use tokio_util::sync::CancellationToken;
use tracing;
// ═══════════════════════════════════════════════════════════════════════════
// PlaylistSourceLogic - Logique pure de lecture de playlist
// ═══════════════════════════════════════════════════════════════════════════
/// Logique pure de lecture de playlist
///
/// Contient seulement la logique de lecture de playlist et décodage des pistes,
/// sans la plomberie d'orchestration (gérée par Node<PlaylistSourceLogic>).
pub struct PlaylistSourceLogic {
playlist_handle: ReadHandle,
cache: Arc<AudioCache>,
chunk_frames: usize,
poll_interval_ms: u64,
history_playlist: Option<Arc<pmoplaylist::WriteHandle>>,
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
}
impl PlaylistSourceLogic {
pub fn new(
playlist_handle: ReadHandle,
cache: Arc<AudioCache>,
chunk_frames: usize,
poll_interval_ms: u64,
) -> Self {
Self {
playlist_handle,
cache,
chunk_frames,
poll_interval_ms,
history_playlist: None,
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
}
}
/// Enregistre une playlist historique pour sauvegarder les morceaux joués
pub fn set_history_playlist(&mut self, history: Arc<pmoplaylist::WriteHandle>) {
self.history_playlist = Some(history);
}
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
}
#[async_trait::async_trait]
impl NodeLogic for PlaylistSourceLogic {
async fn process(
&mut self,
_input: Option<mpsc::Receiver<Arc<AudioSegment>>>,
output: Vec<mpsc::Sender<Arc<AudioSegment>>>,
stop_token: CancellationToken,
) -> Result<(), AudioError> {
tracing::debug!(
"PlaylistSourceLogic::process started, playlist={}, {} children",
self.playlist_handle.id(),
output.len()
);
let node_name = std::any::type_name::<Self>();
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
let mut first_track = true;
loop {
// Vérifier arrêt immédiat
if stop_token.is_cancelled() {
tracing::info!("PlaylistSourceLogic: stop requested, emitting EndOfStream");
let eos = AudioSegment::new_end_of_stream(0, 0.0);
send_to_children(node_name, &output, eos).await?;
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
break;
}
// Pop avec timeout pour supporter stop_token
let track = tokio::select! {
_ = stop_token.cancelled() => {
tracing::info!("PlaylistSourceLogic: stop cancelled during pop");
let eos = AudioSegment::new_end_of_stream(0, 0.0);
send_to_children(node_name, &output, eos).await?;
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
break;
}
result = self.playlist_handle.pop() => {
match result {
Ok(Some(t)) => {
tracing::debug!("PlaylistSourceLogic: popped track from playlist");
t
},
Ok(None) => {
// Playlist vide, attendre avant retry et réinitialiser la synchro
if !first_track {
tracing::debug!(
"PlaylistSourceLogic: playlist drained, resetting top-zero sync"
);
}
first_track = true;
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
tracing::trace!(
"PlaylistSourceLogic: playlist empty, waiting {}ms",
self.poll_interval_ms
);
tokio::time::sleep(
Duration::from_millis(self.poll_interval_ms)
).await;
continue;
}
Err(e) => {
// Erreur playlist (deleted, etc.)
tracing::warn!("PlaylistSourceLogic: playlist error: {}", e);
let error_marker = AudioSegment::new_error(
0,
0.0,
format!("Playlist error: {}", e)
);
send_to_children(node_name, &output, error_marker).await?;
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
continue;
}
}
}
};
// Émettre TrackBoundary avec metadata du cache
let metadata = match track.track_metadata() {
Ok(m) => m,
Err(e) => {
tracing::warn!("PlaylistSourceLogic: failed to get metadata: {}", e);
2025-11-14 10:43:53 +01:00
let error_marker =
AudioSegment::new_error(0, 0.0, format!("Failed to get metadata: {}", e));
send_to_children(node_name, &output, error_marker).await?;
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
continue;
}
};
let metadata_guard = metadata.read().await;
let artist = metadata_guard
.get_artist()
.await
.ok()
.flatten()
.unwrap_or_else(|| "Unknown artist".to_string());
let title = metadata_guard
.get_title()
.await
.ok()
.flatten()
.unwrap_or_else(|| "Untitled".to_string());
2025-11-27 20:27:52 +01:00
let expected_duration = metadata_guard
.get_duration()
.await
.ok()
.flatten()
.map(|d| d.as_secs_f64());
drop(metadata_guard);
let remaining = self.playlist_handle.remaining().await.unwrap_or(0);
tracing::info!(
"PlaylistSource: starting track {} - {} ({} remaining)",
artist,
title,
remaining
);
2025-11-26 18:30:30 +01:00
let track_start = std::time::Instant::now();
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
tracing::debug!("PlaylistSourceLogic: emitting TrackBoundary");
2025-11-27 20:27:52 +01:00
let metadata_for_boundary = metadata.clone();
let boundary = AudioSegment::new_track_boundary(0, 0.0, metadata_for_boundary);
send_to_children(node_name, &output, boundary).await?;
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
// Obtenir le chemin du fichier
let file_path = match track.file_path() {
Ok(p) => p,
Err(e) => {
tracing::warn!("PlaylistSourceLogic: failed to get file path: {}", e);
2025-11-14 10:43:53 +01:00
let error_marker =
AudioSegment::new_error(0, 0.0, format!("Failed to get file path: {}", e));
send_to_children(node_name, &output, error_marker).await?;
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
continue;
}
};
2025-11-26 18:30:30 +01:00
let elapsed = track_start.elapsed();
tracing::info!(
"PlaylistSourceLogic: gap after TrackBoundary = {:.3}s, decoding: {:?}",
elapsed.as_secs_f64(),
file_path
);
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
// Décoder et émettre les chunks PCM
// Passer le cache et pk pour gérer le cache progressif
let cache_pk = track.cache_pk();
// Réinitialiser la synchro au début de chaque piste
let emit_top_zero = true;
first_track = false;
match decode_and_emit_track(
node_name,
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
&file_path,
self.chunk_frames,
&output,
&stop_token,
&self.cache,
cache_pk,
2025-11-27 20:27:52 +01:00
expected_duration,
emit_top_zero,
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
)
.await
{
Ok(()) => {
tracing::info!("PlaylistSource: finished track {} - {}", artist, title);
// Piste décodée avec succès, transférer vers l'historique si configuré
tracing::warn!(
"🔍 HISTORY DEBUG: history_playlist is {:?}",
if self.history_playlist.is_some() {
"Some"
} else {
"None"
}
);
if let Some(ref history) = self.history_playlist {
tracing::warn!(
"🔍 HISTORY DEBUG: Attempting to push cache_pk={} to history",
cache_pk
);
if let Err(e) = history.push(cache_pk.to_string()).await {
tracing::warn!(
"PlaylistSourceLogic: failed to add track to history: {}",
e
);
} else {
tracing::debug!(
"PlaylistSourceLogic: added track {} to history",
cache_pk
);
}
}
}
Err(e) => {
tracing::error!("PlaylistSourceLogic: error decoding track: {}", e);
let error_marker =
AudioSegment::new_error(0, 0.0, format!("Decode error: {}", e));
send_to_children(node_name, &output, error_marker).await?;
// Continue vers la piste suivante
}
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
}
// Boucler pour la piste suivante (pas d'EndOfStream entre pistes !)
}
tracing::debug!("PlaylistSourceLogic::process finished");
Ok(())
}
}
// ═══════════════════════════════════════════════════════════════════════════
// Helper Functions
// ═══════════════════════════════════════════════════════════════════════════
/// Décode un fichier et émet ses chunks audio
///
/// Gère le cache progressif : si EOF est atteint et que le download est toujours en cours,
/// attend et réessaie au lieu de terminer immédiatement.
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
async fn decode_and_emit_track(
node_name: &'static str,
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
path: &PathBuf,
chunk_frames: usize,
output: &[mpsc::Sender<Arc<AudioSegment>>],
stop_token: &CancellationToken,
cache: &Arc<AudioCache>,
cache_pk: &str,
2025-11-27 20:27:52 +01:00
expected_duration_sec: Option<f64>,
emit_top_zero: bool,
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
) -> Result<(), AudioError> {
// Attendre que le fichier soit suffisamment gros pour le sniffing
// Le cache progressif permet de commencer la lecture après le prebuffer (512 KB)
loop {
let metadata = tokio::fs::metadata(path)
.await
.map_err(|e| AudioError::IoError(format!("Failed to stat {:?}: {}", path, e)))?;
let file_size = metadata.len();
const MIN_FILE_SIZE: u64 = 512 * 1024; // 512 KB (prebuffer size)
if file_size >= MIN_FILE_SIZE || cache.is_download_complete(cache_pk) {
2025-11-14 10:43:53 +01:00
tracing::trace!(
"decode_and_emit_track: file ready ({} bytes), starting decode",
file_size
);
break;
}
tracing::trace!(
"decode_and_emit_track: file too small ({} bytes), waiting 50ms...",
file_size
);
tokio::time::sleep(Duration::from_millis(50)).await;
}
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
// Ouvrir et décoder
let file = File::open(path)
.await
.map_err(|e| AudioError::IoError(format!("Failed to open {:?}: {}", path, e)))?;
let mut stream = decode_audio_stream(file)
.await
.map_err(|e| AudioError::ProcessingError(format!("Decode error: {}", e)))?;
let stream_info = stream.info().clone();
// Valider le stream
validate_stream(&stream_info)?;
// Calculer chunk_frames (auto = 50ms)
let chunk_frames = if chunk_frames == 0 {
let frames = (stream_info.sample_rate as f64 * DEFAULT_CHUNK_DURATION_MS / 1000.0) as usize;
frames.next_power_of_two().max(256)
} else {
chunk_frames.max(1)
};
tracing::trace!(
"decode_and_emit_track: sample_rate={}, bit_depth={}, chunk_frames={}",
stream_info.sample_rate,
stream_info.bits_per_sample,
chunk_frames
);
// Lire et émettre les chunks
let frame_bytes = stream_info.bytes_per_sample() * stream_info.channels as usize;
let chunk_byte_len = chunk_frames * frame_bytes;
let mut pending = Vec::new();
let mut read_buf = vec![0u8; frame_bytes * 512.max(chunk_frames)];
let mut chunk_index = 0u64;
let mut total_frames = 0u64;
loop {
tokio::select! {
_ = stop_token.cancelled() => {
tracing::debug!("decode_and_emit_track: stop requested");
break;
}
read_result = stream.read(&mut read_buf) => {
// Remplir le buffer
if pending.len() < chunk_byte_len {
let read = read_result.map_err(|e| {
AudioError::IoError(format!("I/O error while decoding: {}", e))
})?;
// Si EOF atteint (read == 0)
if read == 0 {
Fix progressive cache: distinguish temporary EOF from real EOF Problem: - PlaylistSource reads cached files faster than FlacCacheSink writes them - FLAC decoder encounters EOF and stops playback prematurely - First track doesn't play completely (stops at prebuffer point ~600ms) - Needed to differentiate: * Temporary EOF: file still being written (wait and retry) * Real EOF: file completely written (stop decoding) Solution: 1. Added Cache::is_download_complete() method (pmocache/src/cache.rs:735) - Checks for existence of completion marker (.complete file) - Marker created only when file is fully written and closed - Fast synchronous check (no async overhead) 2. Modified decode_and_emit_track() (playlist_source.rs:337) - On EOF: check if completion marker exists - If no marker: file still being written → wait 50ms and retry read - If marker exists: file complete → finish decoding - Reduced wait from 100ms to 50ms for better responsiveness Benefits: ✅ First track now plays completely (not just prebuffer portion) ✅ Progressive caching still works (playback starts at ~600ms) ✅ Proper EOF handling (no premature stops) ✅ Efficient polling (50ms retry interval) ✅ Works for both fresh downloads and cached files Tested: - Fresh download: EOF retries visible in logs every ~50ms - File plays until completion marker created - No premature track termination Related to previous optimization (commit d8594e7) that made prebuffer→playlist push immediate (76ms instead of 19s).
2025-11-07 13:10:35 +00:00
// Vérifier si le fichier est complètement écrit (completion marker existe)
if !cache.is_download_complete(cache_pk) {
// Fichier encore en cours d'écriture - attendre et réessayer
// Retry plus longtemps pour le cache progressif
tracing::trace!("decode_and_emit_track: EOF but file incomplete, waiting 200ms...");
tokio::time::sleep(Duration::from_millis(200)).await;
continue; // Retry
}
// Completion marker existe - vraie fin du fichier
tracing::trace!("decode_and_emit_track: EOF and file complete");
if pending.is_empty() {
break;
}
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
}
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
if read > 0 {
pending.extend_from_slice(&read_buf[..read]);
}
}
if pending.is_empty() {
break;
}
// Extraire un chunk
let frames_in_pending = pending.len() / frame_bytes;
let frames_to_emit = frames_in_pending.min(chunk_frames);
if frames_to_emit == 0 {
break;
}
let take_bytes = frames_to_emit * frame_bytes;
let chunk_bytes = pending.drain(..take_bytes).collect::<Vec<u8>>();
// Calculer le timestamp
let timestamp_sec = total_frames as f64 / stream_info.sample_rate as f64;
// Créer et envoyer le segment audio
let segment = bytes_to_segment(
&chunk_bytes,
&stream_info,
frames_to_emit,
chunk_index,
timestamp_sec,
)?;
if emit_top_zero && total_frames == 0 {
tracing::debug!("decode_and_emit_track: emitting TopZeroSync (first chunk)");
let top_zero = AudioSegment::new_top_zero_sync();
send_to_children(node_name, output, top_zero).await?;
}
send_to_children(node_name, output, segment).await?;
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
chunk_index += 1;
total_frames += frames_to_emit as u64;
}
}
}
// Traiter le reste éventuel (moins qu'un chunk complet)
if !pending.is_empty() {
let frames = pending.len() / frame_bytes;
if frames > 0 {
let timestamp_sec = total_frames as f64 / stream_info.sample_rate as f64;
2025-11-14 10:43:53 +01:00
let segment =
bytes_to_segment(&pending, &stream_info, frames, chunk_index, timestamp_sec)?;
send_to_children(node_name, output, segment).await?;
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
}
}
// Attendre la fin du décodage
stream
.wait()
.await
.map_err(|e| AudioError::ProcessingError(format!("Decode task failed: {}", e)))?;
if !cache.is_download_complete(cache_pk) {
tracing::warn!(
"PlaylistSource: finished reading cache entry {} but download is not complete",
cache_pk
);
}
2025-11-27 20:27:52 +01:00
let actual_duration = total_frames as f64 / stream_info.sample_rate as f64;
let expected_str = expected_duration_sec
.map(|d| format!("{:.3}s", d))
.unwrap_or_else(|| "unknown".to_string());
tracing::info!(
"PlaylistSource: emitted pk={} frames={} sr={}Hz bit_depth={} duration={:.3}s (expected={})",
cache_pk,
total_frames,
stream_info.sample_rate,
stream_info.bits_per_sample,
actual_duration,
expected_str,
);
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
Ok(())
}
fn validate_stream(info: &StreamInfo) -> Result<(), AudioError> {
if !(1..=2).contains(&info.channels) {
return Err(AudioError::ProcessingError(format!(
"Unsupported channel count: {}",
info.channels
)));
}
match info.bits_per_sample {
8 | 16 | 24 | 32 => Ok(()),
other => Err(AudioError::ProcessingError(format!(
"Unsupported bit depth: {}",
other
))),
}
}
/// Convertit des bytes PCM en AudioSegment avec le type approprié
fn bytes_to_segment(
chunk_bytes: &[u8],
info: &StreamInfo,
frames: usize,
order: u64,
timestamp_sec: f64,
) -> Result<Arc<AudioSegment>, AudioError> {
let bytes_per_sample = info.bytes_per_sample();
let channels = info.channels as usize;
let frame_bytes = bytes_per_sample * channels;
// Créer le chunk du bon type selon la profondeur de bit
let chunk = match info.bits_per_sample {
16 => {
// Type I16
let mut stereo = Vec::with_capacity(frames);
for frame_idx in 0..frames {
let base = frame_idx * frame_bytes;
let l = i16::from_le_bytes(
chunk_bytes[base..base + bytes_per_sample]
.try_into()
.unwrap(),
);
let r = if channels == 1 {
l
} else {
i16::from_le_bytes(
chunk_bytes[base + bytes_per_sample..base + 2 * bytes_per_sample]
.try_into()
.unwrap(),
)
};
stereo.push([l, r]);
}
let chunk_data = AudioChunkData::new(stereo, info.sample_rate, 0.0);
AudioChunk::I16(chunk_data)
}
24 => {
// Type I24
let mut stereo = Vec::with_capacity(frames);
for frame_idx in 0..frames {
let base = frame_idx * frame_bytes;
let l_i32 = {
let mut buf = [0u8; 4];
buf[..3].copy_from_slice(&chunk_bytes[base..base + 3]);
// Sign extend
if chunk_bytes[base + 2] & 0x80 != 0 {
buf[3] = 0xFF;
}
i32::from_le_bytes(buf)
};
let l = I24::new(l_i32).ok_or_else(|| {
AudioError::ProcessingError(format!("Invalid I24 value: {}", l_i32))
})?;
let r = if channels == 1 {
l
} else {
let r_i32 = {
let mut buf = [0u8; 4];
buf[..3].copy_from_slice(
&chunk_bytes[base + bytes_per_sample..base + bytes_per_sample + 3],
);
// Sign extend
if chunk_bytes[base + bytes_per_sample + 2] & 0x80 != 0 {
buf[3] = 0xFF;
}
i32::from_le_bytes(buf)
};
I24::new(r_i32).ok_or_else(|| {
AudioError::ProcessingError(format!("Invalid I24 value: {}", r_i32))
})?
};
stereo.push([l, r]);
}
let chunk_data = AudioChunkData::new(stereo, info.sample_rate, 0.0);
AudioChunk::I24(chunk_data)
}
32 => {
// Type I32
let mut stereo = Vec::with_capacity(frames);
for frame_idx in 0..frames {
let base = frame_idx * frame_bytes;
let l = i32::from_le_bytes(
chunk_bytes[base..base + bytes_per_sample]
.try_into()
.unwrap(),
);
let r = if channels == 1 {
l
} else {
i32::from_le_bytes(
chunk_bytes[base + bytes_per_sample..base + 2 * bytes_per_sample]
.try_into()
.unwrap(),
)
};
stereo.push([l, r]);
}
let chunk_data = AudioChunkData::new(stereo, info.sample_rate, 0.0);
AudioChunk::I32(chunk_data)
}
_ => {
return Err(AudioError::ProcessingError(format!(
"Unsupported bit depth: {}",
info.bits_per_sample
)))
}
};
Ok(Arc::new(AudioSegment {
order,
timestamp_sec,
segment: pmoaudio::_AudioSegment::Chunk(Arc::new(chunk)),
}))
}
// ═══════════════════════════════════════════════════════════════════════════
// WRAPPER PlaylistSource - Délègue à Node<PlaylistSourceLogic>
// ═══════════════════════════════════════════════════════════════════════════
/// PlaylistSource - Lit une playlist et publie des `AudioSegment`
///
/// Cette source utilise une playlist (`ReadHandle`) et le cache audio pour
/// décoder les pistes en continu. Le format de sortie (sample_rate et bit_depth)
/// est **hétérogène** et dépend des fichiers sources.
///
/// Voir la documentation du module pour plus de détails et exemples d'usage.
pub struct PlaylistSource {
inner: Node<PlaylistSourceLogic>,
}
impl PlaylistSource {
/// Crée une nouvelle source de playlist avec paramètres par défaut
///
/// * `playlist_handle` - Handle de lecture sur la playlist
/// * `cache` - Cache audio contenant les fichiers
///
/// Paramètres par défaut :
/// - `chunk_frames` : 0 (auto-calculé pour 50ms)
/// - `poll_interval_ms` : 100ms
pub fn new(playlist_handle: ReadHandle, cache: Arc<AudioCache>) -> Self {
Self::with_config(playlist_handle, cache, 0, 100)
}
/// Crée une nouvelle source de playlist avec configuration personnalisée
///
/// * `playlist_handle` - Handle de lecture sur la playlist
/// * `cache` - Cache audio contenant les fichiers
/// * `chunk_frames` - Nombre de frames par chunk (0 = auto)
/// * `poll_interval_ms` - Intervalle de polling si playlist vide
pub fn with_config(
playlist_handle: ReadHandle,
cache: Arc<AudioCache>,
chunk_frames: usize,
poll_interval_ms: u64,
) -> Self {
2025-11-14 10:43:53 +01:00
let logic =
PlaylistSourceLogic::new(playlist_handle, cache, chunk_frames, poll_interval_ms);
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
Self {
inner: Node::new_source(logic),
}
}
/// Crée une nouvelle source avec playlist historique
///
/// * `playlist_handle` - Handle de lecture sur la playlist live
/// * `cache` - Cache audio contenant les fichiers
/// * `history_playlist` - Handle d'écriture pour l'historique des morceaux joués
///
/// Après avoir joué chaque morceau, il sera automatiquement ajouté à la playlist historique.
/// La playlist historique utilise push() sans TTL, donc les morceaux y restent jusqu'à
/// ce que la capacité maximale soit atteinte (FIFO).
pub fn with_history(
playlist_handle: ReadHandle,
cache: Arc<AudioCache>,
history_playlist: Arc<pmoplaylist::WriteHandle>,
) -> Self {
let mut logic = PlaylistSourceLogic::new(playlist_handle, cache, 0, 100);
logic.set_history_playlist(history_playlist);
Self {
inner: Node::new_source(logic),
}
}
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
}
#[async_trait::async_trait]
impl AudioPipelineNode for PlaylistSource {
fn get_tx(&self) -> Option<mpsc::Sender<Arc<AudioSegment>>> {
self.inner.get_tx()
}
fn register(&mut self, child: Box<dyn AudioPipelineNode>) {
self.inner.register(child)
}
2025-11-14 10:43:53 +01:00
async fn run(self: Box<Self>, stop_token: CancellationToken) -> Result<(), AudioError> {
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
Box::new(self.inner).run(stop_token).await
}
}
impl TypedAudioNode for PlaylistSource {
fn input_type(&self) -> Option<TypeRequirement> {
None // Source n'a pas d'entrée
}
fn output_type(&self) -> Option<TypeRequirement> {
// Format hétérogène - accepte tout
Some(TypeRequirement::any())
}
}
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
#[cfg(test)]
mod tests {
use super::*;
// ═══════════════════════════════════════════════════════════════════════════
// Tests unitaires pour les fonctions helper
// ═══════════════════════════════════════════════════════════════════════════
#[test]
fn test_validate_stream_valid_stereo_16bit() {
let info = StreamInfo {
sample_rate: 44100,
channels: 2,
bits_per_sample: 16,
total_samples: Some(1000),
max_block_size: 4096,
min_block_size: 256,
};
assert!(validate_stream(&info).is_ok());
}
#[test]
fn test_validate_stream_valid_mono_24bit() {
let info = StreamInfo {
sample_rate: 48000,
channels: 1,
bits_per_sample: 24,
total_samples: Some(1000),
max_block_size: 4096,
min_block_size: 256,
};
assert!(validate_stream(&info).is_ok());
}
#[test]
fn test_validate_stream_invalid_channel_count() {
let info = StreamInfo {
sample_rate: 44100,
channels: 5, // Invalid
bits_per_sample: 16,
total_samples: Some(1000),
max_block_size: 4096,
min_block_size: 256,
};
assert!(validate_stream(&info).is_err());
}
#[test]
fn test_validate_stream_invalid_bit_depth() {
let info = StreamInfo {
sample_rate: 44100,
channels: 2,
bits_per_sample: 12, // Invalid
total_samples: Some(1000),
max_block_size: 4096,
min_block_size: 256,
};
assert!(validate_stream(&info).is_err());
}
#[test]
fn test_bytes_to_segment_i16_stereo() {
// Create mock PCM data (2 frames, stereo, 16-bit)
// Frame 1: L=100, R=200
// Frame 2: L=300, R=400
let chunk_bytes = vec![
100u8, 0, // L1
2025-11-14 10:43:53 +01:00
200, 0, // R1
44, 1, // L2 (300 = 0x012C)
144, 1, // R2 (400 = 0x0190)
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
];
let info = StreamInfo {
sample_rate: 44100,
channels: 2,
bits_per_sample: 16,
total_samples: Some(2),
max_block_size: 4096,
min_block_size: 256,
};
let segment = bytes_to_segment(&chunk_bytes, &info, 2, 0, 0.0).unwrap();
assert_eq!(segment.order, 0);
assert_eq!(segment.timestamp_sec, 0.0);
match &segment.segment {
2025-11-14 10:43:53 +01:00
pmoaudio::_AudioSegment::Chunk(chunk) => match chunk.as_ref() {
AudioChunk::I16(data) => {
let frames = data.get_frames();
assert_eq!(frames.len(), 2);
assert_eq!(frames[0], [100, 200]);
assert_eq!(frames[1], [300, 400]);
assert_eq!(data.get_sample_rate(), 44100);
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
}
2025-11-14 10:43:53 +01:00
_ => panic!("Expected I16 chunk"),
},
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
_ => panic!("Expected audio chunk"),
}
}
#[test]
fn test_bytes_to_segment_i16_mono() {
// Create mock PCM data (2 frames, mono, 16-bit)
let chunk_bytes = vec![
100u8, 0, // Frame 1
2025-11-14 10:43:53 +01:00
200, 0, // Frame 2
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
];
let info = StreamInfo {
sample_rate: 48000,
channels: 1,
bits_per_sample: 16,
total_samples: Some(2),
max_block_size: 4096,
min_block_size: 256,
};
let segment = bytes_to_segment(&chunk_bytes, &info, 2, 5, 1.5).unwrap();
assert_eq!(segment.order, 5);
assert_eq!(segment.timestamp_sec, 1.5);
match &segment.segment {
pmoaudio::_AudioSegment::Chunk(chunk) => {
match chunk.as_ref() {
AudioChunk::I16(data) => {
let frames = data.get_frames();
assert_eq!(frames.len(), 2);
// Mono is duplicated to both channels
assert_eq!(frames[0], [100, 100]);
assert_eq!(frames[1], [200, 200]);
}
_ => panic!("Expected I16 chunk"),
}
}
_ => panic!("Expected audio chunk"),
}
}
#[test]
fn test_bytes_to_segment_i24_stereo() {
// Create mock PCM data (1 frame, stereo, 24-bit)
// Frame 1: L=1000 (0x0003E8), R=-1000 (0xFFFC18)
let chunk_bytes = vec![
0xE8, 0x03, 0x00, // L (1000)
0x18, 0xFC, 0xFF, // R (-1000, sign-extended)
];
let info = StreamInfo {
sample_rate: 96000,
channels: 2,
bits_per_sample: 24,
total_samples: Some(1),
max_block_size: 4096,
min_block_size: 256,
};
let segment = bytes_to_segment(&chunk_bytes, &info, 1, 0, 0.0).unwrap();
match &segment.segment {
2025-11-14 10:43:53 +01:00
pmoaudio::_AudioSegment::Chunk(chunk) => match chunk.as_ref() {
AudioChunk::I24(data) => {
let frames = data.get_frames();
assert_eq!(frames.len(), 1);
assert_eq!(frames[0][0].as_i32(), 1000);
assert_eq!(frames[0][1].as_i32(), -1000);
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
}
2025-11-14 10:43:53 +01:00
_ => panic!("Expected I24 chunk"),
},
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
_ => panic!("Expected audio chunk"),
}
}
#[test]
fn test_bytes_to_segment_i32_stereo() {
// Create mock PCM data (1 frame, stereo, 32-bit)
let chunk_bytes = vec![
0x00, 0x10, 0x00, 0x00, // L (4096)
0x00, 0x20, 0x00, 0x00, // R (8192)
];
let info = StreamInfo {
sample_rate: 44100,
channels: 2,
bits_per_sample: 32,
total_samples: Some(1),
max_block_size: 4096,
min_block_size: 256,
};
let segment = bytes_to_segment(&chunk_bytes, &info, 1, 0, 0.0).unwrap();
match &segment.segment {
2025-11-14 10:43:53 +01:00
pmoaudio::_AudioSegment::Chunk(chunk) => match chunk.as_ref() {
AudioChunk::I32(data) => {
let frames = data.get_frames();
assert_eq!(frames.len(), 1);
assert_eq!(frames[0], [4096, 8192]);
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
}
2025-11-14 10:43:53 +01:00
_ => panic!("Expected I32 chunk"),
},
test: Add comprehensive test coverage for PlaylistSource and ResamplingNode This commit adds extensive unit and integration tests for the audio pipeline components that were previously untested. ## ResamplingNode Tests (pmoaudio/src/nodes/resampling_node.rs) - Added 7 test functions covering: - Helper function tests: extract_channels_i16/i24 - Reconstruction tests: reconstruct_chunk_i16/i24 - Logic tests: passthrough when sample rate matches - Async tests: verify sync markers pass through unchanged - Integration test: actual 44.1kHz → 48kHz resampling ## PlaylistSource Tests (pmoaudio-ext/src/sources/playlist_source.rs) - Added 10 test functions covering: - Stream validation: valid/invalid channel counts and bit depths - PCM conversion: bytes_to_segment for I16/I24/I32 formats - Mono/stereo handling: verify channel duplication - Error handling: unsupported bit depth rejection - Type safety: compilation verification ## Bug Fixes - Fixed imports: Node and NodeLogic moved from nodes to pipeline module - Fixed AudioCache import: use pmoaudiocache::Cache with alias - Fixed API calls in ResamplingNode: - BitDepth::from_audio_chunk() → match pattern - .stereo() → .get_frames() - .to_i32() → .as_i32() for I24 - .sample_rate() → .get_sample_rate() ## Documentation - Added INSTALL_LIBSOXR.md with detailed installation instructions - Documents local libsoxr installation without sudo privileges - Provides troubleshooting guide for build and test environments All tests pass successfully (17 tests total: 7 ResamplingNode + 10 PlaylistSource).
2025-11-05 14:16:12 +00:00
_ => panic!("Expected audio chunk"),
}
}
#[test]
fn test_bytes_to_segment_unsupported_bit_depth() {
let chunk_bytes = vec![0u8; 8];
let info = StreamInfo {
sample_rate: 44100,
channels: 2,
bits_per_sample: 8, // Currently unsupported by bytes_to_segment
total_samples: Some(1),
max_block_size: 4096,
min_block_size: 256,
};
let result = bytes_to_segment(&chunk_bytes, &info, 1, 0, 0.0);
assert!(result.is_err());
}
// ═══════════════════════════════════════════════════════════════════════════
// Tests d'intégration pour PlaylistSource
// ═══════════════════════════════════════════════════════════════════════════
// Note: Les tests d'intégration complets nécessitent une vraie playlist et un cache.
// Ces tests peuvent être ajoutés dans un module d'intégration séparé avec des
// fixtures FLAC de test.
#[test]
fn test_playlist_source_type_check() {
// Test de création basique - vérifie que le code compile
// Ce test ne peut pas être exécuté sans mock ou fixture réelles
// car ReadHandle n'implémente pas Clone
use std::sync::Arc;
// Vérification de type - ces lignes ne sont jamais exécutées
if false {
let _handle: ReadHandle = unreachable!();
let _cache: Arc<AudioCache> = unreachable!();
let _source = PlaylistSource::new(_handle, _cache);
}
}
}