Skip to content

feat: streaming first player and time stretching support#6

Merged
jjhafer merged 55 commits into
silvermine:masterfrom
remdalm:feat/streaming-fist-2
May 11, 2026
Merged

feat: streaming first player and time stretching support#6
jjhafer merged 55 commits into
silvermine:masterfrom
remdalm:feat/streaming-fist-2

Conversation

@remdalm
Copy link
Copy Markdown
Contributor

@remdalm remdalm commented Apr 13, 2026

No description provided.

remdalm added 4 commits April 9, 2026 16:29
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.
@remdalm remdalm marked this pull request as draft April 13, 2026 19:40
remdalm added 6 commits April 14, 2026 00:27
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.
@remdalm remdalm force-pushed the feat/streaming-fist-2 branch from e7feb35 to 8626097 Compare April 14, 2026 14:38
@remdalm remdalm force-pushed the feat/streaming-fist-2 branch from 8626097 to 8d45219 Compare April 14, 2026 14:41
@remdalm remdalm changed the title feat: streaming first player feat: streaming first player and time stretching support Apr 14, 2026
@remdalm remdalm force-pushed the feat/streaming-fist-2 branch 2 times, most recently from 7bb125f to 1534e76 Compare May 3, 2026 21:34
remdalm added 9 commits May 4, 2026 21:04
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.
@remdalm remdalm force-pushed the feat/streaming-fist-2 branch from 3f7b167 to 9cd6989 Compare May 4, 2026 19:59
@remdalm remdalm marked this pull request as ready for review May 4, 2026 21:04
Comment thread crates/signalsmith/README.md
Comment thread crates/audio-player/src/player/mod.rs Outdated
Comment thread crates/audio-player/src/player/mod.rs
Comment thread crates/audio-player/src/player/mod.rs
Comment thread crates/audio-player/src/player/http.rs
Comment thread crates/audio-player/src/player/http.rs
Comment thread crates/audio-player/src/player/http.rs
Comment thread crates/audio-player/src/player/stretch.rs
Comment thread crates/audio-player/src/player/mod.rs Outdated
Comment thread crates/audio-player/src/transitions.rs Outdated
Comment thread crates/audio-player/src/player/http.rs Outdated
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));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of returning Ok(...) should we return Err(Error::Http("too many redirects")) once the cap is hit?

Comment thread crates/audio-player/src/player/mod.rs Outdated
Comment thread crates/audio-player/src/player/mod.rs
let seek_strategy = source.seek_strategy;
let resume_fade = source.resume_fade;
ctx.sink.append(source.source);
ctx.position_offset = 0.0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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."

Comment thread crates/audio-player/src/player/source.rs
Comment thread crates/audio-player/src/player/mod.rs Outdated
Comment thread crates/signalsmith/Cargo.toml
@@ -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<()> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread crates/audio-player/src/player/mod.rs Outdated
@remdalm remdalm force-pushed the feat/streaming-fist-2 branch from 4d59263 to 39e0f07 Compare May 10, 2026 20:45
@jjhafer jjhafer merged commit 028ca20 into silvermine:master May 11, 2026
1 check failed
@velocitysystems velocitysystems deleted the feat/streaming-fist-2 branch May 11, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants