diff options
| author | Jake Zerrer <him@jakezerrer.com> | 2025-11-14 09:14:59 -0500 |
|---|---|---|
| committer | Jake Zerrer <him@jakezerrer.com> | 2025-11-14 14:11:33 -0500 |
| commit | c9e4f877402fbadaed1e8e3a0e41125e2188b482 (patch) | |
| tree | 906e3d14cdf9c8f0da233edb7a597edc989e3976 /src/unheard/time_object.clj | |
| parent | 42b1a01f1165ce04cd2addbc44da9e597fcf4ea7 (diff) | |
Create first draft of time-object
Diffstat (limited to 'src/unheard/time_object.clj')
| -rw-r--r-- | src/unheard/time_object.clj | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/src/unheard/time_object.clj b/src/unheard/time_object.clj new file mode 100644 index 0000000..6f088b6 --- /dev/null +++ b/src/unheard/time_object.clj @@ -0,0 +1,122 @@ +(ns unheard.time-object + (:require [missionary.core :as m] + [helins.interval.map :as imap])) + +;; DESIGN +;; A "time object" is any object with a lifetime that is temporally bounded. The +;; goal of the time object abstraction is to allow for for efficient, +;; low-latency allocation and deallocation of an unlimited number of time +;; objects. +;; +;;It is important that a time tree can be efficiently queried by both a range +;;(for UI) and a point (for a song). +;; +;; IMPLEMENTATION +;; Time objects are stored in an interval tree. + +;; Requirements +;; - time objects returned by a timeline range query will include metadata like +;; start time and end time +;; - Flows associated with time objects should only mount or dismount when +;; the result of the query changes. I think the best way to accomplish this +;; is to have the query return a flow of time objects, and then have some +;; separate function responsible for "playing" these time objects. +;; +;; Question: +;; Should time objects take an interval tree as an argument, +;; or should they return a flow of interval information (start, end, value) +;; that can be fed into some kind of reactive interval map bookkeeper? +;; I think the latter. +;; + +(def id-counter (atom 0)) + +(defn time-object + "A time-object takes a start time, and end time, and a value. + Value is a flow that will be consumed when the corresponding + time tree is consumed at a point in time within the time-object's + interval." + [>start >end >metadata >flow] + (let [action (atom nil) + >action (m/watch action) + id (swap! id-counter inc)] + (m/ap + (reset! action + [:add + id + {:start >start + :end >end + :metadata >metadata + + :flow >flow}]) + (try + (m/?< >action) + (catch missionary.Cancelled _ [:remove id]))))) + +(comment + (def cancel + ((m/reduce prn nil (time-object 1 2 :a (m/ap))) + prn prn)) + + (cancel)) + +(defn time-object-collection + "Takes a flow of [diff-action time-object-id time-object], where: + - diff-action is one of either :add or :remove + - time-object-id is a unique identifier + - time-object is the time object in question + + Returns a collection of time objects, represented as a flow." + [& time-objects] + (apply m/latest vector time-objects)) + +(comment + (def cancel + ((m/reduce prn nil + (time-object-collection + (time-object 1 2 :a (m/ap)) + (time-object 3 4 :a (m/ap)))) prn prn)) + + (cancel)) + +;; m/store is an optimization, allowing diffs to be dropped prior to processing +;; by consumer. Think :add 1, :remove 1 + +(defn merge-tocs + "Merge multiple time-object-collections. Returns a new time-object-collection." + [& time-object-colletion]) + +(defn timeline + "Primary timeline bookkeeping mehanism." + [time-object-collection] + (m/ap)) + +(defn point-query + "Query a timeline. Returns a flow of time objects." + [>timeline >at] + (m/ap + (let [[tl at] (m/?< (m/latest vector >timeline >at))] + (get tl at)))) + +(defn range-query + "Range query. Returns a flow of time objects." + [timeline >range]) +(defn run + "Runs the flows associated with a collection of time objects." + ;; TODO: "Running" a time object has different meanings for different objects. + ;; How should I think about that? + [>query-result]) + +;; How should a time tree work? +;; Well, we eventually want to end up with a single time tree +;; Is it important to be able to merge time trees together? +;; Part of me thinks "yes" +;; Time objects cannot be composed together - that is, you can't +;; take two time objects and add them together to get a new time object +;; However, you _can_ add two time objects together to get a time tree +;; The alternative to having intermediate time trees would be to have +;; some kind of time-object-collection abstraction. +;; time-object-collection would have a merge function. +;; A time object collection would be responsible for all of the bookkeeping +;; related to the lifetimes of time objects. +;; My suspicion is that this will |
