Files
2026-07-13 13:13:17 +08:00

160 lines
6.6 KiB
C#

#nullable enable
using System.Diagnostics.CodeAnalysis;
using T3.Core.Animation;
using T3.Core.Audio;
using T3.Core.IO;
using T3.Core.Operator;
using T3.Core.Settings;
using T3.Core.Resource;
using T3.Editor.Gui.UiHelpers;
using T3.Editor.UiModel.ProjectHandling;
namespace T3.Editor.Gui.Interaction.Timing;
public static class PlaybackUtils
{
public static BpmProvider BpmProvider = BpmProvider.Instance;
public static TapProvider TapProvider = TapProvider.Instance;
internal static void UpdatePlaybackAndSyncing()
{
var settings = FindCompositionSettings(out var audioComposition);
WasapiAudioInput.StartFrame(settings);
if (settings.Playback.AudioSource == CompositionSettings.AudioSources.ProjectSoundTrack)
{
// Register every clip in the composition's AudioClips list so the engine
// can play multiple clips simultaneously. The engine's per-clip TimeRange
// bound-check decides which clips are audible at the current playhead.
var time = Playback.Current.TimeInSecs;
foreach (var clip in settings.Playback.AudioClips)
{
if (string.IsNullOrEmpty(clip.AssetPath))
continue;
AudioEngine.UseSoundtrackClip(clip.GetResourceHandle(audioComposition), time);
}
}
// Op-backed audio clips ([AudioClip]) with AutoPlay register themselves here, independent of
// the AudioSource mode. Coexists with the CompositionSettings layer clips above while the
// op-backed audio-clip system is being migrated in.
AudioClipCollector.RegisterAutoPlayClips(ProjectView.Focused?.CompositionInstance,
Playback.Current.TimeInBars,
Playback.Current.TimeInSecs);
if (settings.Playback.AudioSource == CompositionSettings.AudioSources.ExternalDevice
&& settings.Playback.Syncing == CompositionSettings.SyncModes.Tapping)
{
Playback.Current = T3Ui.DefaultBeatTimingPlayback;
if (Playback.Current.Settings is { Playback.Syncing: CompositionSettings.SyncModes.Tapping })
{
if (TapProvider != null)
{
if(TapProvider.BeatTapTriggered)
BeatTiming.TriggerSyncTap();
if (TapProvider.ResyncTriggered)
BeatTiming.TriggerResyncMeasure();
}
Playback.Current.Settings.Playback.Bpm = (float)Playback.Current.Bpm;
// Process callback from [SetBpm] operator
if (BpmProvider != null && BpmProvider.TryGetNewBpmRate(out var newBpmRate2))
{
Log.Debug($" Setting new bpm rate {newBpmRate2}");
BeatTiming.SetBpmRate(newBpmRate2);
}
BeatTiming.Update();
}
}
else
{
Playback.Current = T3Ui.DefaultTimelinePlayback;
}
// Process callback from [SetBpm] operator
if (BpmProvider != null && BpmProvider.TryGetNewBpmRate(out var newBpmRate))
{
Log.Debug($" Applying {newBpmRate} BPM to settings");
settings.Playback.Bpm = newBpmRate;
}
Playback.Current.Bpm = settings.Playback.Bpm;
Playback.Current.Update(UserSettings.Config.EnableIdleMotion);
Playback.Current.Settings = settings;
}
private static CompositionSettings FindCompositionSettings(out IResourceConsumer? owner)
{
var composition = ProjectView.Focused?.CompositionInstance;
if (composition != null && FindCompositionSettingsForInstance(composition, out var instance, out var settings))
{
owner = instance;
return settings;
}
owner = null;
return _defaultCompositionSettings;
}
/// <summary>
/// Scans the current composition path and its parents for a soundtrack
/// </summary>
internal static bool TryFindingSoundtrack([NotNullWhen(true)] out AudioClipResourceHandle? soundtrack,
out IResourceConsumer? composition)
{
var settings = FindCompositionSettings(out composition);
if (composition != null)
return settings.TryGetMainSoundtrack(composition, out soundtrack);
soundtrack = null;
return false;
}
/// <summary>
/// Try to find project settings for an instance by walking up the parent chain.
/// </summary>
/// <returns>false if falling back to default settings</returns>
internal static bool FindCompositionSettingsForInstance(Instance startInstance, out Instance? instanceWithSettings, out CompositionSettings settings)
{
instanceWithSettings = startInstance;
while (true)
{
if (instanceWithSettings == null)
{
settings = _defaultCompositionSettings;
instanceWithSettings = null;
return false;
}
settings = instanceWithSettings.Symbol.CompositionSettings;
if (settings.Enabled)
{
return true;
}
instanceWithSettings = instanceWithSettings.Parent;
}
}
private static readonly CompositionSettings _defaultCompositionSettings = new()
{
Enabled = false,
Playback = new CompositionSettings.PlaybackConfig
{
Bpm = 120,
AudioSource = CompositionSettings.AudioSources.ProjectSoundTrack,
Syncing = CompositionSettings.SyncModes.Timeline,
AudioInputDeviceName = string.Empty,
AudioGainFactor = 1,
AudioDecayFactor = 1,
}
};
}