feat: streaming first player and time stretching support#6
Conversation
Replace direct Symphonia dependency with Rodio's built-in codec features. Implement HTTP streaming via custom `HttpAudioReader` to avoid downloading entire files. Switch from `Sink` to `Player` API and `MixerDeviceSink` for audio output. Remove audio output thread in favor of direct device sink. Add source descriptor abstraction to support both local files and remote URLs with metadata probing. Improve error handling for seek operations and source reopening.
Remove Rodio's built-in codec features and add direct Symphonia dependency for audio decoding. Implement custom `SymphoniaSource` to handle both local files and HTTP streams with precise seeking support. Add position offset tracking to playback context for accurate time reporting during seeks. Split seek logic into separate methods for local and remote sources, with remote seeks creating new player instances at target positions.
Remove Mutex wrapper from HttpResponseReader since the inner reader is already Send + Sync. Add seek generation tracking to prevent race conditions during remote seeks. Refactor seek logic to handle remote seeks asynchronously outside the lock, with proper cancellation support if a new seek is initiated. Improve error recovery for failed remote seeks by restoring previous playback state. Add test for negative seek values clamping to zero.
e7feb35 to
8626097
Compare
8626097 to
8d45219
Compare
7bb125f to
1534e76
Compare
Add preview state for seek operations that updates the UI immediately while dragging the progress bar, with the actual seek committed on release. Implement position clamping helper to prevent seeking beyond track bounds. Update event log key generation to use unique content-based keys instead of array indices. Reset seek preview state when playback stops or becomes idle.
3f7b167 to
9cd6989
Compare
| Err(error) => match *error { | ||
| ureq::Error::Status(status, response) if is_redirect_status(status) => { | ||
| if redirect_count >= HTTP_MAX_REDIRECTS { | ||
| return Ok((current_url, response)); |
There was a problem hiding this comment.
Instead of returning Ok(...) should we return Err(Error::Http("too many redirects")) once the cap is hit?
| let seek_strategy = source.seek_strategy; | ||
| let resume_fade = source.resume_fade; | ||
| ctx.sink.append(source.source); | ||
| ctx.position_offset = 0.0; |
There was a problem hiding this comment.
Quick sanity check: after the loop re-append, position_offset is reset to 0.0, but ctx.sink.get_pos() (used at line 638) appears to be monotonic across appends in rodio. If that's the case, on the second loop iteration audible_sink_pos will be roughly duration (not 0), and pos = 0.0 + audible_sink_pos * playback_rate will report a position beyond the track's duration.
Is there something I'm missing here, or does this need a counterpart adjustment to position_offset (e.g. subtracting the sink's pre-append position) to anchor the new playback span?
Comment added by AI model Claude
There was a problem hiding this comment.
I couldn't reproduce this behaviour during testing in the example Tauri app.
After doing a bit of AI investigation here is what seems to be the consensus: "Since rodio treats the appended item as a new queued sound, and get_pos() reports the position of the currently playing sound, appending a reopened track starts a new playback span rather than continuing a single monotonic timeline."
| @@ -42,10 +42,7 @@ pub fn begin_load(state: &mut PlayerState, src: &str, meta: &AudioMetadata) -> R | |||
| /// `begin_load` was skipped (e.g. instant local file loads). | |||
| pub fn load(state: &mut PlayerState, src: &str, meta: &AudioMetadata, duration: f64) -> Result<()> { | |||
There was a problem hiding this comment.
Small nit: this doc comment says "Also accepts Idle, Ended, and Error in case begin_load was skipped (e.g. instant local file loads)", but the match arm below only accepts Loading (everything else returns Err). Looks like the comment is left over from before the rollback — could it be tightened to reflect the current behavior?
Comment added by AI model Claude
4d59263 to
39e0f07
Compare
No description provided.