using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MuzikaGromche.Via;
interface ILightshow
{
///
/// Override or reset brightness (value) for flickering animation.
///
void SetBrightnessOverride(byte? brightness);
void SetColor(byte hue, byte saturation, byte value);
}
class Animations
{
private static CancellationTokenSource? cts;
public static void Flicker(ILightshow lightshow)
{
if (cts != null)
{
cts.Cancel();
}
cts = new();
_ = Task.Run(async () => await FlickerAsync(lightshow, cts.Token), cts.Token);
}
static async ValueTask FlickerAsync(ILightshow lightshow, CancellationToken cancellationToken)
{
await foreach (var on in FlickerAnimationAsync())
{
if (cancellationToken.IsCancellationRequested)
{
break;
}
byte brightness = on ? (byte)0xFF : (byte)0x00;
lightshow.SetBrightnessOverride(brightness);
}
lightshow.SetBrightnessOverride(null);
}
// Timestamps (in frames of 60 fps) of state switches, starting with 0 => ON.
private static readonly int[] MansionWallLampFlicker = [
0, 4, 8, 26, 28, 32, 37, 41, 42, 58, 60, 71,
];
public static async IAsyncEnumerable FlickerAnimationAsync()
{
bool lastState = false;
int lastFrame = 0;
const int initialMillisecondsDelay = 4 * 1000 / 60;
await Task.Delay(initialMillisecondsDelay);
foreach (int frame in MansionWallLampFlicker)
{
// convert difference in frames into milliseconds
int millisecondsDelay = (int)((frame - lastFrame) / 60f * 1000f);
lastState = !lastState;
lastFrame = frame;
await Task.Delay(millisecondsDelay);
yield return lastState;
}
}
}