forked from nikita/muzika-gromche
Add support for fading out, and debug-only config for flickering lights
This commit is contained in:
parent
b8824dbbfb
commit
118eecbb59
|
@ -76,6 +76,8 @@ namespace MuzikaGromche
|
|||
Bars = 8,
|
||||
LoopOffset = 32,
|
||||
BeatsOffset = 0.0f,
|
||||
FadeOutBeat = -35,
|
||||
FadeOutDuration = 3.3f,
|
||||
ColorTransitionIn = 0.25f,
|
||||
ColorTransitionOut = 0.25f,
|
||||
ColorTransitionEasing = Easing.OutExpo,
|
||||
|
@ -479,6 +481,20 @@ namespace MuzikaGromche
|
|||
// Offset of beats, in seconds. Bigger offset => colors will change later.
|
||||
public float BeatsOffsetInSeconds => BeatsOffset / Beats * LoadedLoop.length;
|
||||
|
||||
public float _FadeOutBeat = float.NaN;
|
||||
public float FadeOutBeat
|
||||
{
|
||||
get => Config.FadeOutBeatOverride ?? _FadeOutBeat;
|
||||
set => _FadeOutBeat = value;
|
||||
}
|
||||
|
||||
public float _FadeOutDuration = 2f;
|
||||
public float FadeOutDuration
|
||||
{
|
||||
get => Config.FadeOutDurationOverride ?? _FadeOutDuration;
|
||||
set => _FadeOutDuration = value;
|
||||
}
|
||||
|
||||
// Duration of color transition, measured in beats.
|
||||
public float _ColorTransitionIn = 0.25f;
|
||||
public float ColorTransitionIn
|
||||
|
@ -504,7 +520,16 @@ namespace MuzikaGromche
|
|||
set => _ColorTransitionEasing = value;
|
||||
}
|
||||
|
||||
public float[] FlickerLightsTimeSeries = [];
|
||||
public float[] _FlickerLightsTimeSeries = [];
|
||||
public float[] FlickerLightsTimeSeries
|
||||
{
|
||||
get => Config.FlickerLightsTimeSeriesOverride ?? _FlickerLightsTimeSeries;
|
||||
set
|
||||
{
|
||||
Array.Sort(value);
|
||||
_FlickerLightsTimeSeries = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float[] LyricsTimeSeries { get; private set; }
|
||||
public string[] LyricsLines { get; private set; }
|
||||
|
@ -846,7 +871,7 @@ namespace MuzikaGromche
|
|||
List<BaseEvent> events = [];
|
||||
|
||||
{
|
||||
var colorEvent = GetColorEvent(windUpOffsetTimestamp);
|
||||
var colorEvent = GetColorEvent(loopOffsetSpan, windUpOffsetTimestamp);
|
||||
if (colorEvent != null)
|
||||
{
|
||||
events.Add(colorEvent);
|
||||
|
@ -873,22 +898,46 @@ namespace MuzikaGromche
|
|||
return events;
|
||||
}
|
||||
|
||||
private SetLightsColorEvent GetColorEvent(BeatTimestamp windUpOffsetTimestamp)
|
||||
private SetLightsColorEvent GetColorEvent(BeatTimeSpan loopOffsetSpan, BeatTimestamp windUpOffsetTimestamp)
|
||||
{
|
||||
if (windUpOffsetTimestamp.Beat < -track.ColorTransitionIn)
|
||||
if (FadeOut(loopOffsetSpan) is Color c1)
|
||||
{
|
||||
// TODO: Maybe fade out?
|
||||
return new SetLightsColorEvent(c1);
|
||||
}
|
||||
else
|
||||
|
||||
if (ColorFromPaletteAtTimestamp(windUpOffsetTimestamp) is Color c2)
|
||||
{
|
||||
var color = ColorFromPaletteAtTimestamp(windUpOffsetTimestamp);
|
||||
return new SetLightsColorEvent(color);
|
||||
return new SetLightsColorEvent(c2);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Color ColorFromPaletteAtTimestamp(BeatTimestamp timestamp)
|
||||
private Color? FadeOut(BeatTimeSpan loopOffsetSpan)
|
||||
{
|
||||
var beat = loopOffsetSpan.BeatToInclusive;
|
||||
var fadeOutStart = track.FadeOutBeat;
|
||||
var fadeOutEnd = fadeOutStart + track.FadeOutDuration;
|
||||
|
||||
if (track.FadeOutBeat < beat && beat <= fadeOutEnd)
|
||||
{
|
||||
var x = (beat - track.FadeOutBeat) / track.FadeOutDuration;
|
||||
var t = Mathf.Clamp(Easing.Linear.Eval(x), 0f, 1f);
|
||||
return Color.Lerp(Color.white, Color.black, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Color? ColorFromPaletteAtTimestamp(BeatTimestamp timestamp)
|
||||
{
|
||||
if (timestamp.Beat <= -track.ColorTransitionIn)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Imagine the timeline as a sequence of clips without gaps where each clip is a whole beat long.
|
||||
// Transition is when two adjacent clips need to be combined with some blend function(t)
|
||||
// where t is a factor in range 0..1 expressed as (time - Transition.Start) / Transition.Length;
|
||||
|
@ -952,7 +1001,7 @@ namespace MuzikaGromche
|
|||
}
|
||||
else
|
||||
{
|
||||
return Color.white;
|
||||
return float.IsNaN(track.FadeOutBeat) ? Color.black : Color.white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1117,6 +1166,9 @@ namespace MuzikaGromche
|
|||
|
||||
public static Palette PaletteOverride { get; private set; } = null;
|
||||
|
||||
public static float? FadeOutBeatOverride { get; private set; } = null;
|
||||
public static float? FadeOutDurationOverride { get; private set; } = null;
|
||||
public static float[] FlickerLightsTimeSeriesOverride { get; private set; } = null;
|
||||
public static float? BeatsOffsetOverride { get; private set; } = null;
|
||||
public static float? ColorTransitionInOverride { get; private set; } = null;
|
||||
public static float? ColorTransitionOutOverride { get; private set; } = null;
|
||||
|
@ -1319,6 +1371,9 @@ namespace MuzikaGromche
|
|||
// Declare and initialize early to avoid "Use of unassigned local variable"
|
||||
List<(Action<Track> Load, Action Apply)> entries = [];
|
||||
SyncedEntry<bool> overrideTimingsSyncedEntry = null;
|
||||
SyncedEntry<float> fadeOutBeatSyncedEntry = null;
|
||||
SyncedEntry<float> fadeOutDurationSyncedEntry = null;
|
||||
SyncedEntry<string> flickerLightsTimeSeriesSyncedEntry = null;
|
||||
SyncedEntry<float> beatsOffsetSyncedEntry = null;
|
||||
SyncedEntry<float> colorTransitionInSyncedEntry = null;
|
||||
SyncedEntry<float> colorTransitionOutSyncedEntry = null;
|
||||
|
@ -1336,6 +1391,12 @@ namespace MuzikaGromche
|
|||
overrideTimingsSyncedEntry.Changed += (sender, args) => apply();
|
||||
overrideTimingsSyncedEntry.SyncHostToLocal();
|
||||
|
||||
fadeOutBeatSyncedEntry = configFile.BindSyncedEntry(section, "Fade Out Beat", 0f,
|
||||
new ConfigDescription("The beat at which to start fading out", new AcceptableValueRange<float>(-1000f, 0)));
|
||||
fadeOutDurationSyncedEntry = configFile.BindSyncedEntry(section, "Fade Out Duration", 0f,
|
||||
new ConfigDescription("Duration of fading out", new AcceptableValueRange<float>(0, 100)));
|
||||
flickerLightsTimeSeriesSyncedEntry = configFile.BindSyncedEntry(section, "Flicker Lights", "",
|
||||
new ConfigDescription("Time series of beat offsets when to flicker the lights"));
|
||||
beatsOffsetSyncedEntry = configFile.BindSyncedEntry(section, "Beats Offset", 0f,
|
||||
new ConfigDescription("How much to offset the whole beat. More is later", new AcceptableValueRange<float>(-0.5f, 0.5f)));
|
||||
colorTransitionInSyncedEntry = configFile.BindSyncedEntry(section, "Color Transition In", 0.25f,
|
||||
|
@ -1346,11 +1407,17 @@ namespace MuzikaGromche
|
|||
new ConfigDescription("Interpolation/easing method to use for color transitions", new AcceptableValueList<string>(Easing.AllNames)));
|
||||
|
||||
var floatSliderOptions = Default(new FloatSliderOptions());
|
||||
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(fadeOutBeatSyncedEntry.Entry, floatSliderOptions));
|
||||
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(fadeOutDurationSyncedEntry.Entry, floatSliderOptions));
|
||||
LethalConfigManager.AddConfigItem(new TextInputFieldConfigItem(flickerLightsTimeSeriesSyncedEntry.Entry, Default(new TextInputFieldOptions())));
|
||||
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(beatsOffsetSyncedEntry.Entry, floatSliderOptions));
|
||||
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(colorTransitionInSyncedEntry.Entry, floatSliderOptions));
|
||||
LethalConfigManager.AddConfigItem(new FloatSliderConfigItem(colorTransitionOutSyncedEntry.Entry, floatSliderOptions));
|
||||
LethalConfigManager.AddConfigItem(new TextDropDownConfigItem(colorTransitionEasingSyncedEntry.Entry, Default(new TextDropDownOptions())));
|
||||
|
||||
registerStruct(fadeOutBeatSyncedEntry, t => t._FadeOutBeat, x => FadeOutBeatOverride = x);
|
||||
registerStruct(fadeOutDurationSyncedEntry, t => t._FadeOutDuration, x => FadeOutDurationOverride = x);
|
||||
registerArray(flickerLightsTimeSeriesSyncedEntry, t => t._FlickerLightsTimeSeries, xs => FlickerLightsTimeSeriesOverride = xs, float.Parse, sort: true);
|
||||
registerStruct(beatsOffsetSyncedEntry, t => t._BeatsOffset, x => BeatsOffsetOverride = x);
|
||||
registerStruct(colorTransitionInSyncedEntry, t => t._ColorTransitionIn, x => ColorTransitionInOverride = x);
|
||||
registerStruct(colorTransitionOutSyncedEntry, t => t._ColorTransitionOut, x => ColorTransitionOutOverride = x);
|
||||
|
@ -1373,6 +1440,34 @@ namespace MuzikaGromche
|
|||
register(syncedEntry, getter, () => setter.Invoke(overrideTimingsSyncedEntry.Value ? syncedEntry.Value : null));
|
||||
void registerClass<T>(SyncedEntry<T> syncedEntry, Func<Track, T> getter, Action<T> setter) where T : class =>
|
||||
register(syncedEntry, getter, () => setter.Invoke(overrideTimingsSyncedEntry.Value ? syncedEntry.Value : null));
|
||||
void registerArray<T>(SyncedEntry<string> syncedEntry, Func<Track, T[]> getter, Action<T[]> setter, Func<string, T> parser, bool sort = false) where T : struct =>
|
||||
register(syncedEntry,
|
||||
(track) => string.Join(", ", getter(track)),
|
||||
() =>
|
||||
{
|
||||
var values = parseStringArray(syncedEntry.Value, parser, sort);
|
||||
if (values != null)
|
||||
{
|
||||
// ensure the entry is sorted and formatted
|
||||
syncedEntry.LocalValue = string.Join(", ", values);
|
||||
}
|
||||
setter.Invoke(overrideTimingsSyncedEntry.Value ? values : null);
|
||||
});
|
||||
|
||||
T[] parseStringArray<T>(string str, Func<string, T> parser, bool sort = false) where T: struct
|
||||
{
|
||||
try
|
||||
{
|
||||
T[] xs = str.Replace(" ", "").Split(",").Select(parser).ToArray();
|
||||
Array.Sort(xs);
|
||||
return xs;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log($"{nameof(MuzikaGromche)} Unable to parse array: {e}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void load()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue