Commit Graph

28 Commits

Author SHA1 Message Date
8924552696 Add StreamType for multi-client support and improved UPnP control
- Introduce `Streamtype` enum (Continuous vs Finite) to distinguish radio streams from finite tracks
- Enrich `TrackBoundary` sync marker with stream type for proper pause behavior per mode (silence vs backpressure)
- Update all sources and sinks to pass `StreamType` when creating track boundaries
  - Radio Paradise, HTTP source → Continuous (infinite)
- Improve UPnP control architecture: pause sends silence for radio, blocks pipeline via backpressure for tracks
- Prepare groundwork for multi-client DSP architecture with shared source and per-DSP pipelines
2026-04-04 14:37:32 +02:00
0a03f72467 Changement du mécanisme d'attention sur les channels Radio Paradise. 2025-12-06 12:48:21 +01:00
3957e17f64 encore des debug... 2025-12-06 12:48:21 +01:00
792ac5cb95 metadonnée technique dans le cache audio 2025-12-06 12:48:21 +01:00
97a383c079 Tentative de gestion d'un historique 2025-11-16 08:02:50 +01:00
1c2d30cbe9 debuggage des stream 2025-11-15 12:21:30 +01:00
Claude
befdd90149 Fix cover caching for all tracks in multi-track Radio Paradise blocks
This commit fixes two critical issues that prevented covers from being
cached for tracks beyond the first one in Radio Paradise blocks:

1. FlacCacheSink Phase 3 metadata loss:
   - When TrackBoundary for track N+1 was received during Phase 3
     of track N, the metadata was discarded
   - Main loop would then wait for a NEW TrackBoundary that never came
   - Solution: Store metadata in next_track_metadata variable and reuse
     it in next iteration
   - Added wait_for_first_audio_chunk() for when metadata is pre-loaded

2. RadioParadiseStreamSource not sending subsequent TrackBoundaries:
   - Code was only checking elapsed_ms >= song.elapsed in loop
   - Added debug logging to track TrackBoundary sending
   - Improved comments explaining first song special handling

Test results:
- Successfully cached covers for 4 consecutive tracks
- Verified with test showing "Successfully cached cover" for each track
- Cover cache directory contains 4 .webp files with complete markers

Files modified:
- pmoaudio-ext/src/sinks/flac_cache_sink.rs
- pmoparadise/src/radio_paradise_stream_source.rs
2025-11-09 10:29:23 +00:00
Claude
c9a71df250 Fix cover caching and playlist persistence in play_and_cache example
Corrige le bug critique qui empêchait la mise en cache des covers pour
les fichiers courts (jingles, etc.) :

Le problème :
- Quand EndOfStream arrivait AVANT la fin du prebuffer, le code retournait
  immédiatement sans copier les métadonnées ni cacher les covers
- Cela affectait particulièrement les fichiers courts (jingles) où le
  prebuffer de 512KB n'était pas atteint avant la fin du fichier

La solution :
- Lorsque EndOfStream est reçu pendant le prebuffer, on ferme le pump mais
  on CONTINUE à attendre que cache_future se termine pour obtenir le pk
- Une fois le pk obtenu, on copie les métadonnées et on cache les covers
  normalement avant de retourner
- Utilise un flag end_of_stream_received et une Option<track_tx> pour gérer
  le cas où track_tx est déjà fermé

Test validé :
✓ Les covers sont bien cachées même pour les fichiers courts
✓ Fichier de cover présent : 36e3e134b8de74e6c16f202e3b3b543d.orig.webp (38K)
✓ Logs montrent : "Successfully cached cover for pk ... with cover pk ..."
2025-11-09 10:06:00 +00:00
Claude
b6723e529d Fix cover caching and playlist persistence in play_and_cache example
Améliore la gestion du cache des covers dans FlacCacheSink :
- Remplace les avertissements génériques par des logs détaillés (debug/info/warn)
- Corrige la gestion des erreurs en retirant le `let _ =` qui ignorait les résultats
- Ajoute des logs de debug pour tracer le processus de mise en cache des covers
- Améliore la gestion des erreurs avec des messages plus informatifs

Corrige la playlist de l'exemple play_and_cache :
- Remplace create_persistent_playlist par get_write_handle pour créer une playlist éphémère
- Une playlist persistante n'est pas nécessaire pour cet exemple de démonstration
2025-11-09 09:32:44 +00:00
Claude
25eb705f59 Increase PCM buffer capacity from 8 to 256 to prevent encoding glitches
The small buffer (8) was causing the pump to block frequently when the FLAC
encoder was slow to consume data. This created micro-pauses in the PCM stream
that resulted in audible clicks in the encoded FLAC files.

With a larger buffer (256), the pump can continue sending data without blocking,
ensuring continuous audio flow to the encoder and eliminating the clicks.
2025-11-08 20:02:04 +00:00
Claude
f1224516e0 Fix Option handling for pump_handle and track_tx to avoid move errors 2025-11-07 16:19:18 +00:00
Claude
413047cce5 Fix FlacCacheSink to not consume TrackBoundary when cache hit
Previous fix consumed the TrackBoundary with drain_until_track_boundary(),
preventing the next track from being processed correctly. This caused
audio to stop after the first cached track.

Solution: Use a pump_closed flag instead of draining. When the pump
closes early (cache hit), set the flag and ignore subsequent chunks
until TrackBoundary. The TrackBoundary is then handled normally by
the existing code, allowing proper continuation to the next track.

This preserves the block structure and allows all tracks in a block
to be processed correctly, whether cached or not.
2025-11-07 16:16:09 +00:00
Claude
483ec26dfe Fix FlacCacheSink error when reusing cached files
When a file was already in cache, add_from_reader() would return
immediately after reading only 1024 bytes to compute the pk. This
closed the flac_stream and pcm_tx, causing the pump to terminate
normally. However, the dispatcher treated track_tx.send() failure
as a fatal error, even though the pump had completed successfully.

Changes:
- In phase 3 post-prebuffer, when track_tx.send() fails, wait for
  pump to complete and check its result
- If pump returned Ok(), drain remaining segments until TrackBoundary
- If pump returned Err(), propagate the error
- This allows graceful handling of cache hits while preserving error
  detection for genuine pump failures

Fixes the "Pump task died" error when relaunching play_and_cache
with existing cached files.
2025-11-07 16:05:29 +00:00
Claude
dba9f668f6 Fix critical deadlock in FlacCacheSink parallel write architecture
Problem: The dispatcher was placed AFTER the prebuffer await, causing a deadlock:
- Pump waits for data on track_rx
- Cache waits for pump to produce PCM data
- Code awaits cache completion before reaching dispatcher
- Dispatcher never runs → pump never receives data → deadlock

Solution: Use tokio::select! to dispatch segments in parallel with prebuffer wait

Architecture now has 3 phases:
1. Phase 1: Dispatch chunks + await prebuffer (in parallel via select!)
2. Phase 2: Copy metadata + push to playlist (after prebuffer complete)
3. Phase 3: Continue dispatching until TrackBoundary

This fixes the "sans musique" blocking issue where the system would freeze
waiting for prebuffer that could never complete.
2025-11-07 14:36:19 +00:00
Claude
4ac81fecac Refactor FlacCacheSink for parallel write tasks to prevent file truncation
Problem: When TrackBoundary arrived, the pump was awaited before continuing,
causing file truncation when pcm_tx was dropped while data was still buffering.

Solution: Allow multiple pump tasks to run in parallel:
- Create dedicated channel (track_tx/track_rx) for each track's pump
- Main loop reads from rx and dispatches segments to current pump via track_tx
- When TrackBoundary arrives: drop track_tx (signals pump to finish) and immediately start new pump
- Old pump continues writing in background until all data is flushed

This prevents truncation in progressive cache scenario (radio streaming).

Changes in flac_cache_sink.rs:
- Replace pump_track_segments_owned() with pump_track_segments_from_channel()
- Remove rx ownership passing - each pump gets its own channel
- Dispatcher loop reads rx and forwards to active pump
- No await on pump completion - let it finish in background
2025-11-07 13:54:23 +00:00
Claude
d8594e72ad Optimize prebuffer→playlist delay: 19s → 76ms (99.6% improvement)
Problem:
- tokio::join!() waited for both cache_future AND pump_future to complete
- cache_future returned after prebuffer (~530ms)
- pump_future read entire first track (~19s)
- Track only pushed to playlist after both finished → 19s delay

Solution (Solution A from OPTIMIZATION_PREBUFFER_TO_PLAYLIST.md):
- Created pump_track_segments_owned() that takes ownership of rx and returns it
- Spawned pump in tokio::spawn to run independently
- Wait for cache_future alone → push to playlist immediately
- Wait for pump_handle later to recover rx for next track

Results (tested with play_and_cache --null-audio):
Before:
  - Prebuffer → playlist: ~19s
  - Prebuffer → playback: ~19.5s

After:
  - Prebuffer → playlist: ~24ms
  - Prebuffer → playback: ~76ms
  - Improvement: 99.6% (250x faster!)

Target was <1s, achieved 76ms (13x better than target!)

Changes:
- Added pump_track_segments_owned() in flac_cache_sink.rs:516
- Modified FlacCacheSinkLogic::process() to use tokio::spawn pattern
- Added timing logs (INFO level) for prebuffer and playlist push
- rx ownership properly managed: moved to pump, returned, recovered

Tests passed:
 Prebuffer completes in ~530ms (512KB downloaded)
 Track pushed to playlist in ~24ms after prebuffer
 Playback starts in ~76ms after prebuffer
 rx properly recovered for next tracks
 No panics or deadlocks
2025-11-07 11:05:47 +00:00
Claude
ed0bbfbf69 Add FlacCacheSink debug logs - system now works!
Added comprehensive logging to FlacCacheSink::process():
- Process start
- Waiting for/receiving first audio chunk
- FLAC encoder creation
- Cache ingestion and pump parallel execution
- tokio::join! completion
- Track added to cache confirmation

Testing results show PROGRESSIVE CACHING WORKS:
 Prebuffer reached in 0.6 seconds
 Track added to cache with pk
 Download pipeline completes successfully
 Playlist receives track
 Playback starts

Current timing:
- t=0.6s: Prebuffer complete (512KB)
- t=3.6s: Track added to playlist (after pump completes)
- t=4.5s: Playback starts

The 3s delay is because tokio::join! waits for BOTH futures:
- cache_future (returns after prebuffer ~0.6s)
- pump_future (pumps entire first track ~3s)

For true 1-2s startup, would need to refactor to push to playlist
immediately after prebuffer, without waiting for pump to complete.
2025-11-07 08:29:20 +00:00
Claude
3bd2a33497 Fix FLAC pk collision by skipping header for pk calculation
Problem: All FLAC files with the same format (44.1kHz, stereo, 16-bit)
had identical headers and thus the same pk (071c5713d5cf485ca688832207bef0f9).
This caused the cache to think all tracks were the same file, regardless
of channel selection or actual content.

Solution: Skip the FLAC header (first 512 bytes) and calculate the pk
from bytes 512-1024 (actual audio content) instead. This ensures each
track gets a unique pk based on its actual audio data, not just its
format header.

Changes:
- Modified add_from_reader_with_pk() to read 1024 bytes instead of 512
- Use bytes 512-1024 for pk calculation when explicit_pk is None
- This works even with poor metadata (empty artist/title)
- Maintains backward compatibility with explicit_pk parameter

Fixes the issue where changing radio channel played the same song.
2025-11-07 06:23:35 +00:00
Claude
c92ad696de Fix playback delay by adding tracks to playlist before draining
When a file was already in cache, FlacCacheSink would drain all
remaining segments (which can take 13+ seconds - the full track
duration) BEFORE adding the track to the playlist. This caused
a long delay before playback could start.

The fix reorders operations to:
1. Copy metadata to cache (fast)
2. Add pk to playlist IMMEDIATELY (fast)
3. Drain remaining segments (slow, but playback already started)

This ensures the playlist receives tracks immediately, allowing
playback to start without waiting for segment drainage to complete.

Fixes the 13-second delay when playing already-cached files.
2025-11-07 06:15:27 +00:00
Claude
f23e43b5ea Implement completion marker system for cache files
- Add .complete marker files to track completed downloads
- Check marker instead of file size for completion detection
- Drain segments when file already in cache to avoid pipeline errors
- Consolidate() now removes incomplete files without markers
- Add new_cache_with_consolidation() for automatic cleanup on startup
2025-11-07 05:43:25 +00:00
Claude
f3d56f4150 Handle gracefully when file is already in cache
Quand un fichier est déjà en cache, add_from_reader() retourne immédiatement
sans lire le stream FLAC, ce qui ferme le channel PCM. Avant cette correction,
pump_track_segments() retournait une erreur SendError, causant l'échec du
pipeline download.

Changements :
- Dans pump_track_segments(), détecter quand le channel est fermé
- Retourner Ok avec StopReason::ChannelClosed au lieu d'une erreur
- Ceci permet au pipeline de se terminer gracieusement

Cette situation est normale et attendue quand le fichier est déjà en cache.
2025-11-06 21:57:16 +00:00
Claude
427c527810 Fix compilation errors in FlacCacheSink streaming implementation
Corrections :
- Removed unused Cursor import
- Fixed borrow checker issues by using tokio::join! instead of tokio::spawn
- Kept progressive streaming approach with add_from_reader

La solution finale utilise tokio::join! pour exécuter pump_track_segments
et add_from_reader en parallèle, évitant ainsi les problèmes de lifetime
avec tokio::spawn tout en conservant le streaming progressif.
2025-11-06 21:49:17 +00:00
Claude
06f514e6c6 Fix streaming and cache progressive in play_and_cache example
Cette correction implémente le cache progressif et le streaming pour permettre
un démarrage quasi immédiat de la lecture pendant le téléchargement.

## Changements dans FlacCacheSink (pmoaudio-ext)

Avant :
- Accumulait tout le FLAC en mémoire dans un buffer
- Attendait la fin complète de l'encodage avant d'ajouter au cache
- Ajoutait à la playlist seulement après ingestion complète

Après :
- Passe le flux FLAC directement à add_from_reader
- add_from_reader retourne dès que le prebuffer (512 KB) est atteint
- Le PK est ajouté à la playlist immédiatement après le prebuffer
- L'encodage et l'écriture continuent en arrière-plan

## Changements dans play_and_cache.rs

- Suppression du sleep de 2 secondes avant le démarrage de la lecture
- Ajout de commentaire expliquant le mécanisme de prebuffer
- La lecture démarre dès que le prebuffer est atteint (~1-2 secondes)

## Résultat

La musique démarre maintenant presque immédiatement après le début du
téléchargement (temps du prebuffer) au lieu d'attendre la fin du
téléchargement complet du premier morceau.
2025-11-06 21:38:29 +00:00
Claude
8b0317f88d refactor: Simplify FlacCacheSink playlist registration using logic_mut()
Improve the architecture by configuring the playlist handle directly
in register_playlist() instead of deferring it to run().

Changes:
- Remove playlist_handle_pending field (no longer needed)
- register_playlist() now calls logic_mut() to configure immediately
- run() becomes a simple delegation with no configuration logic
- Follows proper pattern: configuration before run(), not during run()

This is cleaner than the previous approach which used a pending field
and transferred it during run(). The new approach:
1. User calls register_playlist() → directly configures logic
2. User calls run() → simple delegation to inner.run()

Architecture now properly separates configuration from execution.
2025-11-04 22:33:32 +00:00
Claude
92c53ed3a5 feat: Add logic_mut() to Node and fix playlist registration in FlacCacheSink
- Add Node::logic_mut() method to allow post-construction configuration
  of node logic before run() is called
- Fix FlacCacheSink to properly transfer playlist_handle_pending to
  the inner logic using logic_mut()
- Resolves FIXME at flac_cache_sink.rs:656 about missing logic_mut()

This enables the playlist registration mechanism to work correctly:
1. User calls register_playlist() on FlacCacheSink
2. Handle is stored in playlist_handle_pending
3. During run(), handle is transferred to FlacCacheSinkLogic
4. Tracks are automatically added to playlist after caching
2025-11-04 22:29:32 +00:00
8389d1a78e Récupération de l'erreur git cleaning 2025-11-04 21:13:06 +01:00
0603da2998 Ajout de la gestion des couvertures d'albums par le FlacCacheSink 2025-11-03 20:48:58 +01:00
157baadcfd Ajout d'un noeud puis vers le cache audio 2025-11-03 14:45:37 +01:00