Safe Haskell | None |
---|

The `Signal`

type and functions.

## Synopsis

- data Signal v
- type SignalS v y = Signal (v (Sample y))
- type Interpolate y = Sample y -> Sample y -> X -> y
- data Segment y = Segment {}
- type X = RealTime.RealTime
- data Sample y = Sample {}
- empty :: V.Vector v a => Signal (v a)
- constant :: V.Vector v (Sample y) => y -> SignalS v y
- constant_val :: V.Vector v (Sample a) => SignalS v a -> Maybe.Maybe a
- constant_val_num :: X -> NumSignal -> Maybe.Maybe Y
- beginning :: RealTime.RealTime
- from_vector :: v -> Signal v
- to_vector :: V.Vector v (Sample y) => SignalS v y -> v (Sample y)
- from_samples :: V.Vector v (Sample y) => [Sample y] -> SignalS v y
- to_samples :: V.Vector v (Sample y) => SignalS v y -> [Sample y]
- to_samples_desc :: V.Vector v (Sample y) => SignalS v y -> [Sample y]
- from_pairs :: V.Vector v (Sample y) => [(X, y)] -> SignalS v y
- to_pairs :: V.Vector v (Sample y) => SignalS v y -> [(X, y)]
- to_pairs_desc :: V.Vector v (Sample y) => SignalS v y -> [(X, y)]
- from_segments :: V.Vector v (Sample y) => [Segment y] -> SignalS v y
- to_segments :: V.Vector v (Sample y) => SignalS v y -> [Segment y]
- samples_to_segments :: [Sample y] -> [Segment y]
- simplify :: (Eq x, Eq y) => [(x, y)] -> [(x, y)]
- unfoldr :: V.Vector v (Sample y) => (state -> Maybe.Maybe ((X, y), state)) -> state -> SignalS v y
- with_ptr :: Storable a => Signal (Vector.Storable.Vector a) -> (X -> Ptr a -> Int -> IO b) -> IO b
- null :: V.Vector v (Sample y) => SignalS v y -> Bool
- at :: V.Vector v (Sample y) => Interpolate y -> X -> SignalS v y -> Maybe.Maybe y
- at_negative :: V.Vector v (Sample y) => Interpolate y -> X -> SignalS v y -> Maybe.Maybe y
- segment_at :: V.Vector v (Sample y) => X -> SignalS v y -> Maybe.Maybe (Segment y)
- head :: V.Vector v (Sample y) => SignalS v y -> Maybe.Maybe (X, y)
- last :: V.Vector v (Sample y) => SignalS v y -> Maybe.Maybe (X, y)
- maximum :: (V.Vector v (Sample a), Ord a) => SignalS v a -> Maybe.Maybe a
- minimum :: (V.Vector v (Sample a), Ord a) => SignalS v a -> Maybe.Maybe a
- find :: V.Vector v (Sample y) => (X -> y -> Bool) -> Signal (v (Sample y)) -> Maybe.Maybe (X, y)
- concat :: V.Vector v (Sample y) => Maybe.Maybe (y -> y -> Bool) -> Interpolate y -> [SignalS v y] -> SignalS v y
- prepend :: V.Vector v (Sample y) => Maybe.Maybe (y -> y -> Bool) -> Interpolate y -> SignalS v y -> SignalS v y -> SignalS v y
- drop_after :: V.Vector v (Sample y) => X -> SignalS v y -> SignalS v y
- clip_after :: V.Vector v (Sample y) => Interpolate y -> X -> SignalS v y -> SignalS v y
- num_clip_after :: Bool -> X -> NumSignal -> NumSignal
- drop_before :: V.Vector v (Sample y) => X -> SignalS v y -> SignalS v y
- clip_before :: V.Vector v (Sample y) => Interpolate y -> X -> SignalS v y -> SignalS v y
- clip_before_samples :: V.Vector v (Sample y) => Interpolate y -> X -> SignalS v y -> [Sample y]
- shift :: X -> Signal v -> Signal v
- map_y :: X -> (Y -> Y) -> NumSignal -> NumSignal
- map_y_linear :: V.Vector v (Sample y) => (y -> y) -> SignalS v y -> SignalS v y
- map_x :: V.Vector v (Sample y) => (X -> X) -> SignalS v y -> SignalS v y
- transform_samples :: V.Vector v (Sample y) => ([Sample y] -> [Sample y]) -> SignalS v y -> SignalS v y
- map_err :: V.Vector v (Sample y) => (Sample y -> Either err (Sample y)) -> SignalS v y -> (SignalS v y, [err])
- drop_discontinuity_at :: V.Vector v (Sample y) => X -> SignalS v y -> SignalS v y
- type Boxed y = Signal (TimeVector.Boxed y)
- type NumSignal = Signal TimeVector.Unboxed
- num_interpolate :: Interpolate Y
- num_interpolate_s :: Segment Y -> X -> Y
- invert :: NumSignal -> NumSignal
- integrate :: X -> NumSignal -> NumSignal
- linear_operator :: (Y -> Y -> Y) -> NumSignal -> NumSignal -> NumSignal
- resample_num :: [X] -> [Sample Y] -> [Y]
- resample_maybe :: Interpolate y -> [X] -> [Sample y] -> [Maybe.Maybe y]
- sample_xs :: [[X]] -> [X]
- add_zero_transition :: y -> [Sample y] -> [Sample y]
- to_piecewise_constant :: X -> NumSignal -> TimeVector.Unboxed

# Documentation

A signal modeled as segments. Presumably the segments are linear, but
since you pass your own `Interpolate`

, nothing in this module enforces
that, though there are some transformations that are only valid for linear
segments.

A signal has no value before its first sample, and maintains a constant
value of the last sample forever. There is an implicit discontinuity
to the first sample, so if `x`

is the first sample, then `[(x, y), ..]`

is
implicitly `[(x, Nothing), (x, y), ...]`

. `NumSignal`

uses 0 for not set,
so unless the first `y`

is also 0 it becomes `[(x, 0), (x, y), ..]`

.

This comes with a built-in X offset, so translation is cheap, via `shift`

.

Each X should be >= the previous X, and there shouldn't be more than two
equal Xs in a row. The first ensures that binary search works, and the
second insures that I don't try to interpolate a zero length segment.
Construction via `from_samples`

should establish them, and transformations
should maintain them.

However, a few functions in here can break them, e.g. `map_x`

and `invert`

,
and I think trying to fix them would be expensive. So be careful with
those. Functions should be robust against zero length segments, but if you
break ordering you're out of luck.

If the `y`

value doesn't have an Eq instance, there's no way to filter
out redundant segments like `[(0, 1), (1, 1), (2, 1)]`

. Functions
specialized to `NumSignal`

may make some effort to do that, but only if it
seems worth it.

#### Instances

Eq v => Eq (Signal v) # | |

Show v => Show (Signal v) # | |

DeepSeq.NFData v => DeepSeq.NFData (Signal v) # | |

Defined in Util.Segment | |

Pretty.Pretty v => Pretty.Pretty (Signal v) # | |

Serialize.Serialize v => Serialize.Serialize (Signal v) # | |

type X = RealTime.RealTime Source #

#### Instances

Eq y => Eq (Sample y) # | |

Show y => Show (Sample y) # | |

Storable (Sample Double) # | |

Defined in Util.TimeVectorStorable sizeOf :: Sample Double -> Int # alignment :: Sample Double -> Int # peekElemOff :: Ptr (Sample Double) -> Int -> IO (Sample Double) # pokeElemOff :: Ptr (Sample Double) -> Int -> Sample Double -> IO () # peekByteOff :: Ptr b -> Int -> IO (Sample Double) # pokeByteOff :: Ptr b -> Int -> Sample Double -> IO () # | |

CStorable (Sample Double) # | |

Defined in Util.TimeVectorStorable sizeOf :: Sample Double -> Int # alignment :: Sample Double -> Int # peekElemOff :: Ptr (Sample Double) -> Int -> IO (Sample Double) # pokeElemOff :: Ptr (Sample Double) -> Int -> Sample Double -> IO () # peekByteOff :: Ptr b -> Int -> IO (Sample Double) # pokeByteOff :: Ptr b -> Int -> Sample Double -> IO () # | |

ToJSON (Sample Double) # | |

FromJSON (Sample Double) # | |

Pretty.Pretty y => Pretty.Pretty (Sample y) # | |

Serialize.Serialize y => Serialize.Serialize (Sample y) # | |

# construct / destruct

constant_val :: V.Vector v (Sample a) => SignalS v a -> Maybe.Maybe a Source #

constant_val_num :: X -> NumSignal -> Maybe.Maybe Y Source #

`constant_val`

for `NumSignal`

s can be more clever, because it can compare
Ys. Also NumSignals are implicitly 0 before the first sample.

beginning :: RealTime.RealTime Source #

Use this as the stand-in for "since the beginning of time."

from_vector :: v -> Signal v Source #

from_samples :: V.Vector v (Sample y) => [Sample y] -> SignalS v y Source #

The final sample extends for "all time". However, there's no value before the first sample. The reason is that I'd have to have a zero value for y, and there isn't really an appropriate one for pitch.

TODO I could simplify straight lines, but then I'd need Eq on y. Maybe do that separately for NumSignal.

samples_to_segments :: [Sample y] -> [Segment y] Source #

unfoldr :: V.Vector v (Sample y) => (state -> Maybe.Maybe ((X, y), state)) -> state -> SignalS v y Source #

with_ptr :: Storable a => Signal (Vector.Storable.Vector a) -> (X -> Ptr a -> Int -> IO b) -> IO b Source #

Get a Ptr to the vector. This is `Vector.Storable.unsafeWith`

.

# query

at :: V.Vector v (Sample y) => Interpolate y -> X -> SignalS v y -> Maybe.Maybe y Source #

The arguments may seem backwards, but I've always done it this way, and it seems to be more convenient in practice.

at_negative :: V.Vector v (Sample y) => Interpolate y -> X -> SignalS v y -> Maybe.Maybe y Source #

Like `at`

, but if the x matches a discontinuity, take the value before
instead of after.

segment_at :: V.Vector v (Sample y) => X -> SignalS v y -> Maybe.Maybe (Segment y) Source #

find :: V.Vector v (Sample y) => (X -> y -> Bool) -> Signal (v (Sample y)) -> Maybe.Maybe (X, y) Source #

# concat

:: V.Vector v (Sample y) | |

=> Maybe.Maybe (y -> y -> Bool) | signals with Eq y can drop some redundant samples |

-> Interpolate y | |

-> [SignalS v y] | |

-> SignalS v y |

Concatenate signals, where signals to the right replace the ones to the left where they overlap.

prepend :: V.Vector v (Sample y) => Maybe.Maybe (y -> y -> Bool) -> Interpolate y -> SignalS v y -> SignalS v y -> SignalS v y Source #

With `concat`

, each signal start clips the signal to its left. This is
the other way around, the final sample in the first signal is taken as its
end, and it replaces the start of the second signal.

# slice

drop_after :: V.Vector v (Sample y) => X -> SignalS v y -> SignalS v y Source #

Drop the segments after the given time. The last segment may overlap it.

clip_after :: V.Vector v (Sample y) => Interpolate y -> X -> SignalS v y -> SignalS v y Source #

This is like `drop_after`

, but meant to clip the signal directly on x,
rather than at the first sample >=x. This means I might have to insert a
new sample, which means copying the signal. This is intended to be a "drop
at and after", but since signals extend infinitely to the right, I can only
go up to x. TODO maybe signals should go to Nothing >= the last sample?

If the signal has only a point exactly at x, then return the empty signal. This is because the first sample is effectively a transition from Nothing, or 0.

drop_before :: V.Vector v (Sample y) => X -> SignalS v y -> SignalS v y Source #

Drop the segments before the given time. The first segment will start at or before the given time.

clip_before :: V.Vector v (Sample y) => Interpolate y -> X -> SignalS v y -> SignalS v y Source #

Like `drop_before`

, but ensure that the signal starts exactly at the given
time by splitting a segment that crosses it.

clip_before_samples :: V.Vector v (Sample y) => Interpolate y -> X -> SignalS v y -> [Sample y] Source #

# transform

map_y :: X -> (Y -> Y) -> NumSignal -> NumSignal Source #

Map Ys. This resamples the signal, so it's valid for a nonlinear function.

map_y_linear :: V.Vector v (Sample y) => (y -> y) -> SignalS v y -> SignalS v y Source #

Map Ys. Only valid if the function is linear.

map_x :: V.Vector v (Sample y) => (X -> X) -> SignalS v y -> SignalS v y Source #

Map Xs. The slopes will definitely change unless the function is adding a constant, but presumably that's what you want.

TODO this can break `Signal`

invariants.

transform_samples :: V.Vector v (Sample y) => ([Sample y] -> [Sample y]) -> SignalS v y -> SignalS v y Source #

map_err :: V.Vector v (Sample y) => (Sample y -> Either err (Sample y)) -> SignalS v y -> (SignalS v y, [err]) Source #

## hacks

drop_discontinuity_at :: V.Vector v (Sample y) => X -> SignalS v y -> SignalS v y Source #

Drop a x1==x2 discontinuity at the given time, if there is one. Used for Block.trim_controls, which is a terrible hack that I'm trying to get rid of.

# Boxed

type Boxed y = Signal (TimeVector.Boxed y) Source #

# NumSignal

type NumSignal = Signal TimeVector.Unboxed Source #

num_interpolate_s :: Segment Y -> X -> Y Source #

invert :: NumSignal -> NumSignal Source #

Swap X and Y. Y must be non-decreasing or this will break `Signal`

invariants.

integrate :: X -> NumSignal -> NumSignal Source #

Integrate the signal.

Since the output will have more samples than the input, this needs a sampling rate. The sampling rate determines the resolution of the tempo track. So it can probably be fairly low resolution before having a noticeable impact.

# resample

linear_operator :: (Y -> Y -> Y) -> NumSignal -> NumSignal -> NumSignal Source #

Combine two vectors with the given function. The signals are resampled to have coincident samples, assuming linear interpolation. This only works for linear functions, so the result can also be represented with linear segments.

resample_num :: [X] -> [Sample Y] -> [Y] Source #

resample_maybe :: Interpolate y -> [X] -> [Sample y] -> [Maybe.Maybe y] Source #

This is the same as `resample`

, only for ys without a zero.

sample_xs :: [[X]] -> [X] Source #

The output has the union of the Xs in the inputs, except where they match
exactly. Discontinuities should get two Xs. This is the list version of
`sample_xs2`

.

add_zero_transition :: y -> [Sample y] -> [Sample y] Source #

# piecewise constant

to_piecewise_constant :: X -> NumSignal -> TimeVector.Unboxed Source #