Karya, built on 2023-08-29T07:47:28 (patch 7a412d5d6ba4968ca4155ef276a062ccdeb9109a)
Safe HaskellSafe-Inferred

Derive.Call.ControlUtil

Description

Utilities that emit Signal.Controls and ControlMods.

Synopsis

Documentation

type SRate = RealTime.RealTime Source #

Sampling rate.

data CurveD Source #

Package up a curve name along with arguments.

Constructors

forall arg. CurveD !Text !(Sig.Parser arg) !(arg -> Curve) 

data Curve Source #

Constructors

Function !(Double -> Double) 
Linear

Signals can represent linear segments directly, so if I keep track of them, I can use the efficient direct representation.

type CurveF = Double -> Double Source #

Interpolation function. This maps 0--1 to the desired curve, which is also normalized to 0--1.

interpolator call

type InterpolatorTime a = Either (Sig.Parser DeriveT.Duration) (GetTime a, Text) Source #

Left for an explicit time arg. Right is for an implicit time, inferred from the args, along with an extra bit of documentation to describe it.

from_env :: Sig.Parser (Maybe Signal.Y) Source #

Use this for calls that start from the previous value, to give a way to override that behaviour.

interpolator_variations_ :: Taggable a => (Text -> CurveD -> InterpolatorTime a -> call) -> Expr.Symbol -> CurveD -> [(Expr.Symbol, call)] Source #

Create the standard set of interpolator calls. Generic so it can be used by PitchUtil as well.

curve as argument

curve_env :: Sig.Parser Curve Source #

For calls whose curve can be configured.

pf_linear_name :: Text Source #

A PFunction is a generic function, so it can't retain the distinction between Function and Linear. So I use a grody hack and keep the distinction in a special name.

curve_to_pf :: Text -> Curve -> DeriveT.PFunction Source #

Stuff a curve function into a PFunction.

pf_to_curve :: DeriveT.PFunction -> Curve Source #

Convert a PFunction back into a curve function.

interpolate

place_range :: Typecheck.Normalized -> ScoreTime -> DeriveT.Duration -> Deriver (RealTime.RealTime, RealTime.RealTime) Source #

Given a placement, start, and duration, return the range thus implied.

make_segment_from :: Curve -> RealTime.RealTime -> Maybe Signal.Y -> RealTime.RealTime -> Signal.Y -> Deriver Signal.Control Source #

Make a curve segment from the previous value, if there was one.

segment :: SRate -> Curve -> RealTime.RealTime -> Signal.Y -> RealTime.RealTime -> Signal.Y -> Signal.Control Source #

Interpolate between the given points.

slope

slope_to_limit :: Maybe Signal.Y -> Maybe Signal.Y -> Signal.Y -> Double -> RealTime.RealTime -> RealTime.RealTime -> Signal.Control Source #

Make a line with a certain slope, with optional lower and upper limits. TODO I could support Curve but it would make the intercept more complicated.

exponential

expon :: Double -> CurveF Source #

Negative exponents produce a curve that jumps from the "starting point" which doesn't seem too useful, so so hijack the negatives as an easier way to write 1/n. That way n is smoothly departing, while -n is smoothly approaching.

expon2 :: Double -> Double -> CurveF Source #

I could probably make a nicer curve of this general shape if I knew more math.

bezier

guess_x :: (Double -> (Double, Double)) -> CurveF Source #

As far as I can tell, there's no direct way to know what value to give to the bezier function in order to get a specific x. So I guess with binary search. https://youtu.be/aVwxzDHniEw?t=1119

sigmoid :: Double -> Double -> CurveF Source #

Generate a sigmoid curve. The first weight is the flatness at the start, and the second is the flatness at the end. Both should range from 0--1.

bezier3 :: Point -> Point -> Point -> Point -> Double -> Point Source #

Cubic bezier curve.

breakpoints

breakpoints :: SRate -> Curve -> [(RealTime.RealTime, Signal.Y)] -> Signal.Control Source #

Create line segments between the given breakpoints.

signal_breakpoints :: Monoid sig => (RealTime.RealTime -> y -> sig) -> (RealTime.RealTime -> y -> RealTime.RealTime -> y -> sig) -> [(RealTime.RealTime, y)] -> sig Source #

distribute :: RealTime.RealTime -> RealTime.RealTime -> [a] -> [(RealTime.RealTime, a)] Source #

Distribute the values evenly over the given time range.

smooth

smooth_absolute Source #

Arguments

:: Curve 
-> RealTime.RealTime 
-> RealTime.RealTime

If negative, each segment is from this much before the original sample until the sample. If positive, it starts on the sample. If samples are too close, the segments are shortened correspondingly.

-> [(RealTime.RealTime, Signal.Y)] 
-> Signal.Control 

Use the function to create a segment between each point in the signal. Smooth with split_samples_absolute.

split_samples_absolute :: RealTime.RealTime -> [(RealTime.RealTime, y)] -> [(RealTime.RealTime, y)] Source #

Split apart samples to make a flat segment.

TODO if y=Pitch there's no Eq, so breakpoints winds up sampling flat segments. I could emit Maybe y where Nothing means same as previous.

0 1 2 3 4 5 6 7 8
0-------1-------0
0-----0=1-----1=0      time = -1
0-------0=1-----1=0    time = 1

split_samples_relative :: ScoreT.Function -> [(RealTime.RealTime, y)] -> [(RealTime.RealTime, y)] Source #

Like smooth_absolute, but the transition time is a 0--1 proportion of the available time, rather than an absolute time. Also, the transition is always before the destination sample, unlike absolute, where it's only before for a negative transition time. This is because I can't transition after the sample, because the last sample has no next sample to take a proportional time from!

0 1 2 3 4 5 6 7 8
0-------1-------0
0-----0=1-----1=0 time_at = const 0.25

control mod

modify_with Source #

Arguments

:: Merge 
-> ScoreT.Control 
-> RealTime.RealTime

Where the modification should end. I don't need a start time since signals already have an implicit start time.

-> Signal.Control 
-> Deriver () 

Modify the signal only in the given range.