Describe the bug
With the default waveform_options, a gr.Audio(streaming=True) output plays the first stream fine but every run after that is silent. The component re-renders and the server side is fine (each run gets its own run id, MediaStream and playlist URL), but the player never attaches a new source.
waveform_options=gr.WaveformOptions(show_recording_waveform=False) works around it.
Reproduction
from io import BytesIO
import gradio as gr
import numpy as np
from pydub import AudioSegment
def stream():
for i in range(6):
t = np.arange(i * 8000, (i + 1) * 8000)
pcm = (0.2 * np.sin(2 * np.pi * 440 * t / 16000) * 32767).astype("<i2")
seg = AudioSegment(pcm.tobytes(), frame_rate=16000, sample_width=2, channels=1)
buf = BytesIO()
seg.export(buf, format="wav")
yield buf.getvalue()
with gr.Blocks() as demo:
out = gr.Audio(streaming=True, autoplay=True)
gr.Button("Stream").click(stream, None, out)
if __name__ == "__main__":
demo.launch()
Click Stream, let it finish, then click it again. The first run plays, the second does not. Add waveform_options=gr.WaveformOptions(show_recording_waveform=False) to the gr.Audio and both runs play.
Confirmed in Chromium: with the workaround the second run gets a new MediaSource, without it the first one is reused.
Cause
In js/audio/player/AudioPlayer.svelte, stream_active is only reset inside load_audio():
https://github.com/gradio-app/gradio/blob/ec369a583/js/audio/player/AudioPlayer.svelte#L233-L234
and load_stream() returns immediately while it is set:
https://github.com/gradio-app/gradio/blob/ec369a583/js/audio/player/AudioPlayer.svelte#L253-L256
The effect that calls load_audio() is gated on (waveform_ready || !waveform_options.show_recording_waveform):
https://github.com/gradio-app/gradio/blob/ec369a583/js/audio/player/AudioPlayer.svelte#L296-L306
For a stream the waveform branch is deliberately not rendered, since use_waveform is show_recording_waveform && !value?.is_stream, so container never exists, create_waveform() never runs, and waveform_ready never becomes true. With the default show_recording_waveform: true both sides of the guard are false, load_audio() never runs, stream_active stays true from the first run, and every later load_stream() early-returns.
The !waveform_options.show_recording_waveform escape hatch came from #13728 and only covers the disabled-waveform player, which is why that setting works around this. Gating on the existing use_waveform derived instead of on waveform_options.show_recording_waveform looks like it would cover the streaming case too, though I have not checked what else that guard affects.
Severity
I can work around it
Describe the bug
With the default
waveform_options, agr.Audio(streaming=True)output plays the first stream fine but every run after that is silent. The component re-renders and the server side is fine (each run gets its own run id,MediaStreamand playlist URL), but the player never attaches a new source.waveform_options=gr.WaveformOptions(show_recording_waveform=False)works around it.Reproduction
Click Stream, let it finish, then click it again. The first run plays, the second does not. Add
waveform_options=gr.WaveformOptions(show_recording_waveform=False)to thegr.Audioand both runs play.Confirmed in Chromium: with the workaround the second run gets a new
MediaSource, without it the first one is reused.Cause
In
js/audio/player/AudioPlayer.svelte,stream_activeis only reset insideload_audio():https://github.com/gradio-app/gradio/blob/ec369a583/js/audio/player/AudioPlayer.svelte#L233-L234
and
load_stream()returns immediately while it is set:https://github.com/gradio-app/gradio/blob/ec369a583/js/audio/player/AudioPlayer.svelte#L253-L256
The effect that calls
load_audio()is gated on(waveform_ready || !waveform_options.show_recording_waveform):https://github.com/gradio-app/gradio/blob/ec369a583/js/audio/player/AudioPlayer.svelte#L296-L306
For a stream the waveform branch is deliberately not rendered, since
use_waveformisshow_recording_waveform && !value?.is_stream, socontainernever exists,create_waveform()never runs, andwaveform_readynever becomes true. With the defaultshow_recording_waveform: trueboth sides of the guard are false,load_audio()never runs,stream_activestaystruefrom the first run, and every laterload_stream()early-returns.The
!waveform_options.show_recording_waveformescape hatch came from #13728 and only covers the disabled-waveform player, which is why that setting works around this. Gating on the existinguse_waveformderived instead of onwaveform_options.show_recording_waveformlooks like it would cover the streaming case too, though I have not checked what else that guard affects.Severity
I can work around it