AudioStreamPlayer "hiccups" when I update its stream's data

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By fella

Hey, I’m trying to make a game with an online radio station tuner, but every time I update the stream data with a fresh chunk the playback stops. Therefore, I’m forced to play(). This results in very choppy audio.

If using large buffers for the stream, it will hiccup less frequently, but this will result in a long initial buffer time. This is still not ideal. I have tried to union stream.data with mp3_stream.data, but playback stops. No matter how I try to edit AudioStreamMP3’s data, it will always stop the playback.

I have tried masking the hiccup with static and it sounds janky as heck.

I’m hoping there is way to mitigate this issue. It’s driving me nuts. Do you think it’s worth creating a second AudioStreamPlayer to switch between the two? I’d imagine it’d be difficult to sync these up.

Thanks!

extends AudioStreamPlayer

export(String) var station_name
export(String) var station_endpoint_url = "74.208.71.58"
export(int, 0, 65535) var station_endpoint_port = 8087
export(int, 0, 256) var buffer_kb = 64
export(float, 0, 1, 0.01) var frequency = .5
export(float, 87.9, 107.9, 0.1) var channel = 97.9
export(float, .05, .15) var bandwidth = .1
export(float, 0, 1) var signal_strength = 1
export(String, MULTILINE) var http_request = "GET / HTTP/1.1\n\n"
var mp3_stream = AudioStreamMP3.new()

var streaming = true
var stream_peer = StreamPeerTCP.new()

func _ready():
	http_request = http_request.to_utf8()
	stream_peer.connect_to_host(station_endpoint_url, station_endpoint_port)
	
func _physics_process(delta):
	if streaming:
		stream_peer.put_data(http_request)
		if stream_peer.get_status() == stream_peer.STATUS_CONNECTED:
			var available_bytes = stream_peer.get_available_bytes()

 			# When payload is ready, update the stream
			if available_bytes >= buffer_kb * 1000:
				mp3_stream.data = stream_peer.get_data(available_bytes)[1]
				print("*hiccup*")
				stream = mp3_stream
				play()

Thanks for sharing. I have added some probing + main scene

Probing


signal data_available(d)

func _process(delta):
	if stream != null:
		emit_signal("data_available", {
			'position' : get_playback_position(),
			'available': stream_peer.get_available_bytes(),
			'stream': stream.get_length()
		})

...
			printt("*hiccup*", get_playback_position())
...

Main


extends Control

onready var button:Button = $Button
onready var station = $AudioStreamPlayer
onready var label:Label = $Label

func _ready():
	button.connect("button_up", self, 'play')
	station.connect("data_available", self, 'data_available')

func play():
	station.play()

func data_available(d:Dictionary):
	label.text = String(d.position) + '\n' + String(d.available) + '\n' + String(d.stream)

You are requesting data every physics FPS (60/120?) which seems a lot to me.

The hickup itself is caused by overriding a playing stream not yet ended. Is that fixable?

The app disfunction after a while too.

E 0:06:12.942 get_available_bytes: Condition “!is_open()” is true. Returned: -1
<C++ Source> drivers/unix/net_socket_posix.cpp:705 @ get_available_bytes()
AudioStreamPlayer.gd:28 @ _process()

clemens.tolboom | 2021-07-14 13:13

Error is because connection was closed.

Regarding number of request:

First data gets on request number 16!

16 Bytes available 454
17 Bytes available 4678
24 Bytes available 6086
25 Bytes available 14534
32 Bytes available 17350
33 Bytes available 34246
40 Bytes available 39878
41 Bytes available 63814
48 Bytes available 66126

To fix this only ‘open’ the request once


var has_connection:bool = false
...
			if not has_connection:
				stream_peer.put_data(http_request)
				has_connection = true
...

clemens.tolboom | 2021-07-14 13:47

There are other stream types like AudioStreamGeneratorPlayback which got discussed in AudioStreamGeneratorPlayback delay · Issue #46490 · godotengine/godot · GitHub … in the end they suggest to use a Thread (for VOIP) but I guess for this we only need to append to the buffer.

Maybe this https://github.com/godotengine/godot-demo-projects/tree/master/audio/generator helps.

Please let me/us know your solutions :slight_smile:

clemens.tolboom | 2021-07-14 15:11