use std::hash::Hash;
use std::ops::Range;
use std::sync::atomic::{AtomicU64, Ordering};
use crate::geometry::Vec2;
use crate::phase::Phase;
use crate::raster::{RasterImage, Resolution};
use crate::render_context::RenderContext;
use crate::time::Time;
use crate::window::Window;
use super::*;
static EVENT_COUNTER: AtomicU64 = AtomicU64::new(0);
#[derive(Debug, Clone, Copy)]
pub struct Event {
id: u64,
name: Option<&'static str>,
}
impl PartialEq for Event {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Event {}
impl Hash for Event {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Event {
pub fn new() -> Self {
Self {
id: EVENT_COUNTER.fetch_add(1, Ordering::Relaxed),
name: None,
}
}
pub fn named(name: &'static str) -> Self {
Self {
id: EVENT_COUNTER.fetch_add(1, Ordering::Relaxed),
name: Some(name),
}
}
pub fn id(&self) -> u64 {
self.id
}
pub fn name(&self) -> Option<&'static str> {
self.name
}
pub fn is_before(&self, clock: &Clock<'_>) -> bool {
clock.global().seconds() < clock.trigger_of(*self).seconds()
}
pub fn is_after(&self, clock: &Clock<'_>) -> bool {
clock.global().seconds() >= clock.trigger_of(*self).seconds()
}
pub fn phase(&self, clock: &Clock<'_>, a: f32, b: f32) -> Phase {
let trigger = clock.trigger_of(*self).seconds();
if trigger.is_infinite() {
return Phase::ZERO;
}
clock.global().phase(trigger + a, trigger + b)
}
pub fn elapsed(&self, clock: &Clock<'_>) -> f32 {
let trigger = clock.trigger_of(*self).seconds();
if trigger.is_infinite() {
return 0.0;
}
(clock.global().seconds() - trigger).max(0.0)
}
pub fn window(&self, clock: &Clock<'_>, range: Range<f32>) -> Window {
assert!(
range.start.is_finite() && range.end.is_finite() && range.end > range.start,
"Event::window requires a finite range with end > start"
);
let trigger = clock.trigger_of(*self).seconds();
if trigger.is_infinite() {
return Window::new(range.start, range.end, range.start);
}
let start = trigger + range.start;
let end = trigger + range.end;
Window::new(start, end, clock.global().seconds()).clamped()
}
}
impl Default for Event {
fn default() -> Self {
Self::new()
}
}
pub struct Triggered<T> {
child: T,
event: Event,
kind: TriggerKind,
}
impl<T: PartialEq> PartialEq for Triggered<T> {
fn eq(&self, other: &Self) -> bool {
self.child == other.child && self.event == other.event && self.kind == other.kind
}
}
impl<T: Eq> Eq for Triggered<T> {}
impl<T: Hash> Hash for Triggered<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.child.hash(state);
self.event.hash(state);
self.kind.hash(state);
}
}
#[derive(Debug, Clone, Copy, crate::Keyable)]
enum TriggerKind {
Start,
End,
At(f32),
}
impl<T> Triggered<T> {
pub fn child(&self) -> &T {
&self.child
}
pub fn event(&self) -> Event {
self.event
}
}
impl<T: TimelineComponent + PartialEq + Hash + 'static> TimelineComponent for Triggered<T> {
fn duration(&self) -> Option<f32> {
self.child.duration()
}
fn measure(&self) -> Option<f32> {
self.child.measure()
}
fn resolve(&self, abs_start: f32, out: &mut ResolveCtx) -> f32 {
let len = self.child.resolve(abs_start, out);
let scale = out.local_scale;
let at = match self.kind {
TriggerKind::Start => abs_start,
TriggerKind::End => abs_start + len * scale,
TriggerKind::At(local) => abs_start + local * scale,
};
out.triggers_mut().record(self.event, at);
len
}
fn frame(
&self,
clock: Clock<'_>,
canvas: Vec2,
target: Resolution,
ctx: &mut dyn RenderContext,
) -> Option<RasterImage> {
self.child.frame(clock, canvas, target, ctx)
}
fn samples(&self, clock: Clock<'_>, window: f32) -> Option<AudioBuffer> {
self.child.samples(clock, window)
}
fn mix_into(&self, mix: &mut crate::audio::AudioMix, start_secs: f32, speed: f32) {
self.child.mix_into(mix, start_secs, speed);
}
fn cues(&self, offset: f32) -> Vec<Cue> {
self.child.cues(offset)
}
fn arrangement(&self, offset: f32) -> Arrangement {
let mut node = self.child.arrangement(offset);
let at = match self.kind {
TriggerKind::Start => offset,
TriggerKind::End => node.end,
TriggerKind::At(local) => offset + local,
};
node.triggers.push(TriggerMark {
time: at,
name: self.event().name().map(::std::string::String::from),
});
node
}
}
impl<T> From<Triggered<T>> for Box<dyn TimelineComponent + Send>
where
T: TimelineComponent + PartialEq + Hash + Send + 'static,
{
fn from(triggered: Triggered<T>) -> Self {
Box::new(triggered)
}
}
pub struct Sourced {
source: &'static ::core::panic::Location<'static>,
inner: Box<dyn TimelineComponent + Send>,
}
impl Sourced {
pub fn new(
source: &'static ::core::panic::Location<'static>,
inner: Box<dyn TimelineComponent + Send>,
) -> Self {
Self { source, inner }
}
pub fn source_loc(&self) -> SourceLoc {
SourceLoc {
file: self.source.file().to_owned(),
line: self.source.line(),
}
}
}
pub fn peel_source(
child: &(dyn TimelineComponent + Send),
) -> (Option<SourceLoc>, &(dyn TimelineComponent + Send)) {
let mut source = None;
let mut cur = child;
while let Some(sourced) = cur.as_any().downcast_ref::<Sourced>() {
source = Some(sourced.source_loc());
cur = sourced.inner.as_ref();
}
(source, cur)
}
impl PartialEq for Sourced {
fn eq(&self, other: &Self) -> bool {
*self.inner == *other.inner
}
}
impl Eq for Sourced {}
impl Hash for Sourced {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl TimelineComponent for Sourced {
fn duration(&self) -> Option<f32> {
self.inner.duration()
}
fn measure(&self) -> Option<f32> {
self.inner.measure()
}
fn resolve(&self, abs_start: f32, out: &mut ResolveCtx) -> f32 {
self.inner.resolve(abs_start, out)
}
fn frame(
&self,
clock: Clock<'_>,
canvas: Vec2,
target: Resolution,
ctx: &mut dyn RenderContext,
) -> Option<RasterImage> {
self.inner.frame(clock, canvas, target, ctx)
}
fn samples(&self, clock: Clock<'_>, window: f32) -> Option<AudioBuffer> {
self.inner.samples(clock, window)
}
fn mix_into(&self, mix: &mut crate::audio::AudioMix, start_secs: f32, speed: f32) {
self.inner.mix_into(mix, start_secs, speed);
}
fn cues(&self, offset: f32) -> Vec<Cue> {
self.inner.cues(offset)
}
fn arrangement(&self, offset: f32) -> Arrangement {
let mut node = self.inner.arrangement(offset);
if node.source.is_none() {
node.source = Some(SourceLoc {
file: self.source.file().to_owned(),
line: self.source.line(),
});
}
node
}
fn structural_any(&self) -> &dyn std::any::Any {
self.inner.structural_any()
}
}
impl From<Sourced> for Box<dyn TimelineComponent + Send> {
fn from(sourced: Sourced) -> Self {
Box::new(sourced)
}
}
pub trait Triggers: TimelineComponent + Sized {
fn trigger_at_start(self, e: Event) -> Triggered<Self> {
Triggered {
child: self,
event: e,
kind: TriggerKind::Start,
}
}
fn trigger_at_end(self, e: Event) -> Triggered<Self> {
Triggered {
child: self,
event: e,
kind: TriggerKind::End,
}
}
fn trigger_at(self, local: f32, e: Event) -> Triggered<Self> {
Triggered {
child: self,
event: e,
kind: TriggerKind::At(local),
}
}
}
impl<T: TimelineComponent + Sized> Triggers for T {}
pub trait TriggersBuilder: TimelineBuilder {
fn trigger_at_start(self, e: Event) -> Triggered<Self::Output> {
self.build_component().trigger_at_start(e)
}
fn trigger_at_end(self, e: Event) -> Triggered<Self::Output> {
self.build_component().trigger_at_end(e)
}
fn trigger_at(self, local: f32, e: Event) -> Triggered<Self::Output> {
self.build_component().trigger_at(local, e)
}
}
impl<B: TimelineBuilder> TriggersBuilder for B {}