On runtime, I want to play 2 audio streams*, one after the other, without even a frame of delay between them. These streams are not decided beforehand.
These audio streams are actually 2 halves of a music track, so the transition must be perfect or it will produce a noticeable audio “click”.
This doesn’t make a seamless transition. There’s a small gap between the end of one stream and the start of the next.
- Using a playlist or interactable:
Unless I’m missing something, these don’t allow the streams they contain to be set at runtime, so I can’t use them since the streams must be chosen at runtime.
I actually managed a solution that so far seems to work, but seems extremely clunky for something that should be pretty basic.
The solution is to import the first audiostream with loop mode forward, then manually check each frame if the first audio stream is finished or about to finish, and trigger the change:
Thanks mrcdk. That almost worked, except for my use case I need to be able to update the playlist on the fly (adding the second stream while the first one is playing), but seems that calling set_list_stream makes the audio player stop.
I tried getting the current playback position before updating the playlist, then seeking to that point right after, but that produces a noticeable jump.
extends AudioStreamPlayer
var streams:Array[AudioStream] = [
preload("res://assets/part1.ogg"),
preload("res://assets/part2.ogg"),
preload("res://assets/part3.ogg"),
]
var current_stream = 0
var playback:AudioStreamGeneratorPlayback
var playbacks:Array[AudioStreamPlayback] = []
func _ready() -> void:
var generator = AudioStreamGenerator.new()
generator.mix_rate_mode = AudioStreamGenerator.MIX_RATE_OUTPUT
stream = generator
play()
playback = get_stream_playback() as AudioStreamGeneratorPlayback
playbacks.push_back(streams[0].instantiate_playback())
await get_tree().create_timer(0.32).timeout
playbacks.push_back(streams[1].instantiate_playback())
await get_tree().create_timer(0.63).timeout
playbacks.push_back(streams[2].instantiate_playback())
func _process(delta: float) -> void:
mix()
func mix() -> void:
var available = playback.get_frames_available()
while available > 0:
var stream_playback = playbacks[current_stream]
if not stream_playback.is_playing():
stream_playback.start()
var to_mix = stream_playback.mix_audio(1.0, available)
var mixed = to_mix.size()
if mixed > 0:
playback.push_buffer(to_mix)
if mixed < available:
current_stream = wrap(current_stream + 1, 0, playbacks.size())
available -= mixed
There’s a small noticeable pop when mixing the streams and I’m not sure why, though. Maybe because I just cut the original audio track at random points? dunno.