Skip to content

Conversation

@sfc-gh-nbellante
Copy link
Contributor

@sfc-gh-nbellante sfc-gh-nbellante commented Aug 19, 2025

Configurable audio sample rates for st.audio_input

Overview

Adds support for configurable audio sample rates in st.audio_input, letting apps pick recording quality per use case (e.g., 16 kHz for ASR, 48 kHz for music).

While I'm here, this PR also fixes #12446

Changes

Python API

  • sample_rate (optional) added to st.audio_input(...), default 16 kHz.
  • Validates against: 8000, 16000, 22050, 44100, 48000 Hz.
  • Raises StreamlitAPIException on invalid values.

Frontend

  • Media constraints: pass sampleRate: { ideal: targetSampleRate } to getUserMedia.
  • Guaranteed resampling: use OfflineAudioContext to convert to the exact target rate.
  • Why both? Devices may ignore ideal; resampling enforces consistency.

Technical details

  • Removed incorrect audioBitsPerSecond usage (bitrate ≠ sample rate).
  • Removed invalid audioContext option from RecordPlugin.
  • Pass constraints to startRecording() (not the plugin constructor).

Testing

  • Unit::
    • Error handling (null/empty blobs, missing APIs)
    • WebKit fallbacks
    • Up/down-sampling across 6 rate pairs
    • Channel configs: mono, stereo, 4-ch, 5.1
  • E2E: verifies sample-rate options render and work
  • Coverage: frontend ≈ 86%

Example usage

# Speech recognition optimized
audio_bytes = st.audio_input("Record speech", sample_rate=16000)

# High-quality music recording
audio_bytes = st.audio_input("Record music", sample_rate=48000)

# Default browser sample rate
audio_bytes = st.audio_input("Record audio", sample_rate=None)

Files changed

  • lib/streamlit/elements/widgets/audio_input.py — Python API
  • frontend/lib/src/components/widgets/AudioInput/AudioInput.tsx — React updates
  • frontend/lib/src/components/widgets/AudioInput/convertAudioToWav.ts — resampling
  • frontend/lib/src/components/widgets/AudioInput/convertAudioToWav.test.tsnew tests
  • lib/tests/streamlit/elements/audio_input_test.py — Python tests
  • e2e_playwright/st_audio_input*.py — E2E coverage

Notes

  • sampleRate is an ideal getUserMedia constraint; actual device rate may differ.
  • Resampling ensures the output matches the requested rate across browsers/devices.

@snyk-io
Copy link
Contributor

snyk-io bot commented Aug 19, 2025

🎉 Snyk checks have passed. No issues have been found so far.

security/snyk check is complete. No issues have been found. (View Details)

license/snyk check is complete. No issues have been found. (View Details)

@github-actions
Copy link
Contributor

github-actions bot commented Aug 19, 2025

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-12272/streamlit-1.49.1-py3-none-any.whl
🕹️ Preview app pr-12272.streamlit.app (☁️ Deploy here if not accessible)

@sfc-gh-nbellante sfc-gh-nbellante added security-assessment-completed Security assessment has been completed for PR change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users labels Aug 19, 2025

This comment was marked as outdated.

@github-actions
Copy link
Contributor

github-actions bot commented Aug 19, 2025

📉 Frontend coverage change detected

The frontend unit test (vitest) coverage has decreased by 0.0900%

  • Current PR: 84.8500% (47319 lines, 7166 missed)
  • Latest develop: 84.9400% (47139 lines, 7099 missed)

💡 Consider adding more unit tests to maintain or improve coverage.

📊 View detailed coverage comparison

@sfc-gh-nbellante sfc-gh-nbellante changed the title [feat] Add sample rate configuration to audio input component and tests [feat] Add sample rate parameter to st.audio_input component Sep 3, 2025
@sfc-gh-nbellante sfc-gh-nbellante changed the title [feat] Add sample rate parameter to st.audio_input component [feat] Add sample rate parameter to st.audio_input component Sep 4, 2025

This comment was marked as outdated.

@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/audio-input-sample-rate branch from 1db321e to 1545062 Compare September 4, 2025 17:15

This comment was marked as outdated.

@sfc-gh-nbellante sfc-gh-nbellante marked this pull request as ready for review September 5, 2025 18:30
@sfc-gh-nbellante sfc-gh-nbellante requested a review from a team as a code owner September 5, 2025 18:30
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/audio-input-sample-rate branch from aa9bbff to 60ec96a Compare September 9, 2025 17:44
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/audio-input-sample-rate branch from 90d11de to 02dbf67 Compare September 10, 2025 16:49
arrayBuffer = await fileBlob.arrayBuffer()
} catch (error) {
LOG.error("Failed to read blob as ArrayBuffer", error)
void audioContext.close()
Copy link
Collaborator

Choose a reason for hiding this comment

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

question: Should we await for the .close() to succeed before returning?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm are you thinking perhaps it's only not an issue right now cause fast computer? I was thinking it works while not waiting for it to finish (and we no longer depend on it) so why make the user wait for it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Those are good points! My original question was more towards the syntax code smell of using void vs await. I would expect to use await in most scenarios, and only use void when we really don't care about the side-effect. I see your perspective from your last comment, so I think this is fine in practice.

…g to prevent unnecessary re-renders.

and
        setRecordingTime(formatTime(0))
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 8 out of 12 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

frontend/lib/src/components/widgets/AudioInput/convertAudioToWav.ts:1

  • The global regex replacement /:/g will replace all colons in the timestamp, but ISO timestamps contain multiple colons (e.g., '2025-01-15T10:30:45'). This could result in malformed filenames. Consider using a more specific replacement or sanitizing the entire timestamp for filename safety.
/**

Copy link
Collaborator

@sfc-gh-bnisco sfc-gh-bnisco left a comment

Choose a reason for hiding this comment

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

Thanks for the revisions!

@sfc-gh-nbellante sfc-gh-nbellante merged commit b502b3a into develop Sep 10, 2025
38 checks passed
@sfc-gh-nbellante sfc-gh-nbellante deleted the nico/audio-input-sample-rate branch September 10, 2025 22:50
@RonaldAJ
Copy link

Thanks for making this! I haven't had the time to test it, but reading bits and pieces of code I see the right digital signal processing terminology, and the usual of by one bug fix so commonly occurring in this type of work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users security-assessment-completed Security assessment has been completed for PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audio Input Waveform Colors Not Updating When Recording After Seek/Pause

4 participants