summaryrefslogtreecommitdiff
path: root/src/unheard/time_object.clj
diff options
context:
space:
mode:
Diffstat (limited to 'src/unheard/time_object.clj')
-rw-r--r--src/unheard/time_object.clj122
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