mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-11 05:38:34 +00:00
start porting pattern
This commit is contained in:
parent
615d441381
commit
b5bafc8f60
310
js/strudel.mjs
310
js/strudel.mjs
@ -1,13 +1,28 @@
|
||||
import Fraction from 'fraction.js'
|
||||
|
||||
// Returns the start of the cycle.
|
||||
Fraction.prototype.sam = function() {
|
||||
return Fraction(Math.floor(this))
|
||||
}
|
||||
|
||||
// Returns the start of the next cycle.
|
||||
Fraction.prototype.nextSam = function() {
|
||||
return this.sam().add(1)
|
||||
}
|
||||
|
||||
// Returns a TimeSpan representing the begin and end of the Time value's cycle
|
||||
Fraction.prototype.wholeCycle = function() {
|
||||
return new TimeSpan(this.sam(), this.nextSam())
|
||||
}
|
||||
|
||||
Fraction.prototype.lt = function(other) {
|
||||
return this.compare(other) < 0
|
||||
}
|
||||
|
||||
Fraction.prototype.gt = function(other) {
|
||||
return this.compare(other) > 0
|
||||
}
|
||||
|
||||
Fraction.prototype.lte = function(other) {
|
||||
return this.compare(other) <= 0
|
||||
}
|
||||
@ -35,8 +50,8 @@ class TimeSpan {
|
||||
break
|
||||
}
|
||||
// add a timespan up to the next sam
|
||||
var next_begin = begin.next_sam()
|
||||
spans.push(TimeSpan(begin, next_begin))
|
||||
var next_begin = begin.nextSam()
|
||||
spans.push(new TimeSpan(begin, next_begin))
|
||||
|
||||
// continue with the next cycle
|
||||
begin = next_begin
|
||||
@ -126,4 +141,293 @@ class Hap {
|
||||
}
|
||||
}
|
||||
|
||||
export {TimeSpan, Hap}
|
||||
class Pattern {
|
||||
constructor(query) {
|
||||
this.query = query
|
||||
}
|
||||
|
||||
|
||||
|
||||
split_queries() {
|
||||
// Splits queries at cycle boundaries. This makes some calculations
|
||||
// easier to express, as all events are then constrained to happen within
|
||||
// a cycle.
|
||||
var query = function(span) {
|
||||
return span.spanCycles.map(subspan => this.query(subspan)).concat
|
||||
}
|
||||
return new Pattern(query)
|
||||
}
|
||||
// def with_query_span(self, func):
|
||||
// """ Returns a new pattern, with the function applied to the timespan of the query. """
|
||||
// return Pattern(lambda span: self.query(func(span)))
|
||||
|
||||
// def with_query_time(self, func):
|
||||
// """ Returns a new pattern, with the function applied to both the begin
|
||||
// and end of the the query timespan. """
|
||||
// return Pattern(lambda span: self.query(span.with_time(func)))
|
||||
|
||||
// def with_event_span(self, func):
|
||||
// """ Returns a new pattern, with the function applied to each event
|
||||
// timespan. """
|
||||
// def query(span):
|
||||
// return [event.with_span(func) for event in self.query(span)]
|
||||
// return Pattern(query)
|
||||
|
||||
// def with_event_time(self, func):
|
||||
// """ Returns a new pattern, with the function applied to both the begin
|
||||
// and end of each event timespan.
|
||||
// """
|
||||
// return self.with_event_span(lambda span: span.with_time(func))
|
||||
|
||||
// def with_value(self, func):
|
||||
// """Returns a new pattern, with the function applied to the value of
|
||||
// each event. It has the alias 'fmap'.
|
||||
|
||||
// """
|
||||
// def query(span):
|
||||
// return [event.with_value(func) for event in self.query(span)]
|
||||
// return Pattern(query)
|
||||
|
||||
// # alias
|
||||
// fmap = with_value
|
||||
|
||||
// def _filter_events(self, event_test):
|
||||
// return Pattern(lambda span: list(filter(event_test, self.query(span))))
|
||||
|
||||
// def _filter_values(self, value_test):
|
||||
// return Pattern(lambda span: list(filter(lambda event: value_test(event.value), self.query(span))))
|
||||
|
||||
// def onsets_only(self):
|
||||
// """Returns a new pattern that will only return events where the start
|
||||
// of the 'whole' timespan matches the start of the 'part'
|
||||
// timespan, i.e. the events that include their 'onset'.
|
||||
// """
|
||||
// return(self._filter_events(Event.has_onset))
|
||||
|
||||
// #applyPatToPatLeft :: Pattern (a -> b) -> Pattern a -> Pattern b
|
||||
// #applyPatToPatLeft pf px = Pattern q
|
||||
// # where q st = catMaybes $ concatMap match $ query pf st
|
||||
// # where
|
||||
// # match ef = map (withFX ef) (query px $ st {arc = wholeOrPart ef})
|
||||
// # withFX ef ex = do let whole' = whole ef
|
||||
// # part' <- subArc (part ef) (part ex)
|
||||
// # return (Event (combineContexts [context ef, context ex]) whole' part' (value ef $ value ex))
|
||||
|
||||
// def _app_whole(self, whole_func, pat_val):
|
||||
// """
|
||||
// Assumes self is a pattern of functions, and given a function to
|
||||
// resolve wholes, applies a given pattern of values to that
|
||||
// pattern of functions.
|
||||
// """
|
||||
// pat_func = self
|
||||
// def query(span):
|
||||
// event_funcs = pat_func.query(span)
|
||||
// event_vals = pat_val.query(span)
|
||||
// def apply(event_funcs, event_vals):
|
||||
// s = event_funcs.part.intersection(event_vals.part)
|
||||
// if s == None:
|
||||
// return None
|
||||
// return Event(whole_func(event_funcs.whole, event_vals.whole), s, event_funcs.value(event_vals.value))
|
||||
// return concat([remove_nones([apply(event_func, event_val) for event_val in event_vals])
|
||||
// for event_func in event_funcs
|
||||
// ]
|
||||
// )
|
||||
// return Pattern(query)
|
||||
|
||||
// # A bit more complicated than this..
|
||||
// def app_both(self, pat_val):
|
||||
// """ Tidal's <*> """
|
||||
// def whole_func(span_a, span_B):
|
||||
// if span_a == None or span_B == None:
|
||||
// return None
|
||||
// return span_a.intersection_e(span_B)
|
||||
|
||||
// return self._app_whole(whole_func, pat_val)
|
||||
|
||||
// def app_left(self, pat_val):
|
||||
// pat_func = self
|
||||
// def query(span):
|
||||
// events = []
|
||||
// for event_func in pat_func.query(span):
|
||||
// event_vals = pat_val.query(event_func.part)
|
||||
// for event_val in event_vals:
|
||||
// new_whole = event_func.whole
|
||||
// new_part = event_func.part.intersection_e(event_val.part)
|
||||
// new_value = event_func.value(event_val.value)
|
||||
// events.append(Event(new_whole, new_part, new_value))
|
||||
// return events
|
||||
// return Pattern(query)
|
||||
|
||||
// def app_right(self, pat_val):
|
||||
// pat_func = self
|
||||
// def query(span):
|
||||
// events = []
|
||||
// for event_val in pat_val.query(span):
|
||||
// event_funcs = pat_func.query(event_val.part)
|
||||
// for event_func in event_funcs:
|
||||
// new_whole = event_val.whole
|
||||
// new_part = event_func.part.intersection_e(event_val.part)
|
||||
// new_value = event_func.value(event_val.value)
|
||||
// events.append(Event(new_whole, new_part, new_value))
|
||||
// return events
|
||||
// return Pattern(query)
|
||||
|
||||
// def __add__(self, other):
|
||||
// return self.fmap(lambda x: lambda y: x + y).app_left(reify(other))
|
||||
|
||||
// def __radd__(self, other):
|
||||
// return self.__add__(other)
|
||||
|
||||
// def __sub__(self, other):
|
||||
// return self.fmap(lambda x: lambda y: x - y).app_left(reify(other))
|
||||
|
||||
// def __rsub__(self, other):
|
||||
// raise ValueError # or return NotImplemented?
|
||||
|
||||
// def union(self, other):
|
||||
// return self.fmap(lambda x: lambda y: {**x, **y}).app_left(other)
|
||||
|
||||
// def __rshift__(self, other):
|
||||
// """Overrides the >> operator to support combining patterns of
|
||||
// dictionaries (AKA 'control patterns'). Produces the union of
|
||||
// two patterns of dictionaries, with values from right replacing
|
||||
// any with the same name from the left
|
||||
// """
|
||||
// return self.union(other)
|
||||
|
||||
// def __lshift__(self, other):
|
||||
// """Like >>, but matching values from left replace those on the right"""
|
||||
// return self.fmap(lambda x: lambda y: {**y, **x}).app_left(other)
|
||||
|
||||
// def _bind_whole(self, choose_whole, func):
|
||||
// pat_val = self
|
||||
// def query(span):
|
||||
// def withWhole(a, b):
|
||||
// return Event(choose_whole(a.whole, b.whole), b.part,
|
||||
// b.value
|
||||
// )
|
||||
// def match(a):
|
||||
// return [withWhole(a, b) for b in func(a.value).query(a.part)]
|
||||
|
||||
// return concat([match(ev) for ev in pat_val.query(span)])
|
||||
// return Pattern(query)
|
||||
|
||||
// def bind(self, func):
|
||||
// def whole_func(a, b):
|
||||
// if a == None or b == None:
|
||||
// return None
|
||||
// return a.intersection_e(b)
|
||||
// return self._bind_whole(whole_func, func)
|
||||
|
||||
// def join(self):
|
||||
// """Flattens a pattern of patterns into a pattern, where wholes are
|
||||
// the intersection of matched inner and outer events."""
|
||||
// return self.bind(id)
|
||||
|
||||
// def inner_bind(self, func):
|
||||
// def whole_func(a, b):
|
||||
// return a
|
||||
// return self._bind_whole(whole_func, func)
|
||||
|
||||
// def inner_join(self):
|
||||
// """Flattens a pattern of patterns into a pattern, where wholes are
|
||||
// taken from inner events."""
|
||||
// return self.inner_bind(id)
|
||||
|
||||
// def outer_bind(self, func):
|
||||
// def whole_func(a, b):
|
||||
// return b
|
||||
// return self._bind_whole(whole_func, func)
|
||||
|
||||
// def outer_join(self):
|
||||
// """Flattens a pattern of patterns into a pattern, where wholes are
|
||||
// taken from outer events."""
|
||||
// return self.outer_bind(id)
|
||||
|
||||
// def _patternify(method):
|
||||
// def patterned(self, *args):
|
||||
// pat_arg = sequence(*args)
|
||||
// return pat_arg.fmap(lambda arg: method(self,arg)).outer_join()
|
||||
// return patterned
|
||||
|
||||
// def _fast(self, factor):
|
||||
// """ Speeds up a pattern by the given factor"""
|
||||
// fastQuery = self.with_query_time(lambda t: t*factor)
|
||||
// fastEvents = fastQuery.with_event_time(lambda t: t/factor)
|
||||
// return fastEvents
|
||||
// fast = _patternify(_fast)
|
||||
|
||||
// def _slow(self, factor):
|
||||
// """ Slow slows down a pattern """
|
||||
// return self._fast(1/factor)
|
||||
// slow = _patternify(_slow)
|
||||
|
||||
// def _early(self, offset):
|
||||
// """ Equivalent of Tidal's <~ operator """
|
||||
// offset = Fraction(offset)
|
||||
// return self.with_query_time(lambda t: t+offset).with_event_time(lambda t: t-offset)
|
||||
// early = _patternify(_early)
|
||||
|
||||
// def _late(self, offset):
|
||||
// """ Equivalent of Tidal's ~> operator """
|
||||
// return self._early(0-offset)
|
||||
// late = _patternify(_late)
|
||||
|
||||
// def when(self, binary_pat, func):
|
||||
// binary_pat = sequence(binary_pat)
|
||||
// true_pat = binary_pat._filter_values(id)
|
||||
// false_pat = binary_pat._filter_values(lambda val: not val)
|
||||
// with_pat = true_pat.fmap(lambda _: lambda y: y).app_right(func(self))
|
||||
// without_pat = false_pat.fmap(lambda _: lambda y: y).app_right(self)
|
||||
// return stack(with_pat, without_pat)
|
||||
|
||||
// def off(self, time_pat, func):
|
||||
// return stack(self, self.early(time_pat))
|
||||
|
||||
// def every(self, n, func):
|
||||
// pats = [func(self)] + ([self] * (n-1))
|
||||
// return slowcat(*pats)
|
||||
|
||||
// def append(self, other):
|
||||
// return fastcat(self,other)
|
||||
|
||||
// def rev(self):
|
||||
// def query(span):
|
||||
// cycle = span.begin.sam()
|
||||
// next_cycle = span.begin.next_sam()
|
||||
// def reflect(to_reflect):
|
||||
// reflected = to_reflect.with_time(lambda time: cycle + (next_cycle - time))
|
||||
// (reflected.begin, reflected.end) = (reflected.end, reflected.begin)
|
||||
// return reflected
|
||||
// events = self.query(reflect(span))
|
||||
// return [event.with_span(reflect) for event in events]
|
||||
// return Pattern(query).split_queries()
|
||||
|
||||
// def jux(self, func, by=1):
|
||||
// by = by / 2
|
||||
// def elem_or(dict, key, default):
|
||||
// if key in dict:
|
||||
// return dict[key]
|
||||
// return default
|
||||
|
||||
// left = self.with_value(lambda val: dict(list(val.items()) + [("pan", elem_or(val, "pan", 0.5) - by)]))
|
||||
// right = self.with_value(lambda val: dict(list(val.items()) + [("pan", elem_or(val, "pan", 0.5) + by)]))
|
||||
|
||||
// return stack(left,func(right))
|
||||
|
||||
// def first_cycle(self):
|
||||
// return self.query(TimeSpan(Fraction(0), Fraction(1)))
|
||||
|
||||
// def __repr__(self):
|
||||
// return str(self.first_cycle())
|
||||
}
|
||||
|
||||
function pure(value) {
|
||||
// Returns a pattern that repeats the given value once per cycle
|
||||
function query(span) {
|
||||
return span.spanCycles.map(subspan => new Hap(Fraction(subspan.begin).wholeCycle(), subspan, value))
|
||||
}
|
||||
return new Pattern(query)
|
||||
}
|
||||
|
||||
export {TimeSpan, Hap, Pattern, pure, Fraction}
|
||||
1048
package-lock.json
generated
1048
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user