using Godot; namespace Rokojori; [Tool] [GlobalClass] public partial class MusicChannelPlayer:Node { [Export] public MusicChannel channel; [Export] public MusicData currentMusic; [Export] public AudioStreamPlayer currentPlayer; public void TweenVolume( AudioStreamPlayer target, float duration, float toVolume, bool clear, System.Action onReady = null ) { var tl = TimeLineManager.Ensure( null ); var start = tl.position; var fromVolume = target.VolumeLinear; TimeLineManager.ScheduleSpanIn( tl, 0, duration, ( span, type )=> { if ( ! GodotObject.IsInstanceValid( target ) ) { return; } var lerpedVolume = Mathf.Lerp( fromVolume, toVolume, span.phase ); target.VolumeLinear = lerpedVolume; if ( type == TimeLineSpanUpdateType.End && clear ) { if ( clear ) { ClearAudioPlayer( target ); } if ( onReady != null ) { onReady(); } } }, this ); } async void ClearAudioPlayer( AudioStreamPlayer target ) { await this.RequestNextFrame(); target.DeleteSelf(); } public void SchedulePlayer( MusicData nextMusic, float duration, System.Action onReady ) { if ( currentMusic != null ) { this.LogInfo( "Muting:", currentMusic ); TweenVolume( currentPlayer, duration, 0f, true ); currentMusic = null; currentPlayer = null; } this.LogInfo( "Playing:", nextMusic ); var name = FilePath.Absolute( nextMusic.audio.ResourcePath ).fileName; var nextPlayer = this.CreateChild( "Player " + name ); nextPlayer.VolumeLinear = 0f; nextPlayer.Stream = nextMusic.audio; nextPlayer.PitchScale = MathAudio.SemitonesToFrequencyScaler( nextMusic.pitchSemitonesOffset ); nextPlayer.Bus = channel.audioBus; currentMusic = nextMusic; currentPlayer = nextPlayer; TweenVolume( nextPlayer, duration, nextMusic.volume, false, onReady ); nextPlayer.Play(); } }