Skip to content

Commit ce46507

Browse files
SchlenkRSchlenkR
authored andcommitted
stuff
1 parent 0517adf commit ce46507

16 files changed

Lines changed: 1396 additions & 70 deletions

README.md

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ LocSta provides composable, stateful stream blocks using F# computation expressi
1818

1919
## Core Concept
2020

21-
A `SigStream<'v,'c,'s>` is a function that takes a `StateController` and a context value, and returns an output value along with the updated state. The `stream { }` computation expression composes these blocks, automatically managing state allocation and threading.
21+
A `SigStream<'v,'c,'s>` is a function that takes a `StateController` and a context value, and returns a **sequence of output values** along with the updated state. The `stream { }` computation expression composes these blocks, automatically managing state allocation and threading. Streams can emit zero, one, or multiple values per tick using `yield`.
2222

2323
```fsharp
2424
open LocSta.Core
@@ -42,15 +42,18 @@ let result = myCounter |> Eval.run 5 (fun _ -> ())
4242
| Primitive | Description |
4343
|---|---|
4444
| `stream { }` | Computation expression builder for composing streams |
45+
| `return` / `yield` | Emit a single value (`yield` allows multi-emit with `Combine`) |
46+
| `yield!` | Forward all values from a sub-stream |
4547
| `getCtx()` | Read the current input/context value |
4648
| `useState value` | Local mutable state, initialized once |
4749
| `useStateWith init` | Local mutable state with lazy initializer |
4850
| `useMemoWith init` | Memoized value (lazy, computed once) |
49-
| `ofSeq sequence` | Create a stream from a sequence |
51+
| `ofSeq sequence` | Create a stream from a sequence (emits empty when exhausted) |
5052
| `map proj stream` | Transform stream output |
51-
| `Eval.run n getCtx stream` | Evaluate n samples with a context generator |
53+
| `Eval.run n getCtx stream` | Evaluate n values with a context generator |
5254
| `Eval.runWith inputs stream` | Evaluate with an input sequence |
53-
| `Eval.toSeq getCtx stream` | Convert to an infinite `seq<'v>` |
55+
| `Eval.toSeq getCtx stream` | Convert to a lazy `seq<'v>` (stops after 1000 silent ticks) |
56+
| `Eval.toSeqWith max getCtx stream` | Same with custom silent-tick limit |
5457

5558
## Examples
5659

@@ -212,6 +215,85 @@ let result = countWhere (fun x -> x > 4) |> Eval.runWith inputs
212215

213216
</details>
214217

218+
<details>
219+
<summary><strong>TimeSeries — Types</strong></summary>
220+
221+
| Type | Description |
222+
|---|---|
223+
| `DataPoint<'v>` | A value with a `DateTimeOffset` timestamp |
224+
| `ResampleContext<'v>` | Window of DataPoints with optional before/after neighbors |
225+
| `Resampler<'v,'r>` | `ResampleContext<'v> -> 'r` — aggregation or interpolation function |
226+
227+
</details>
228+
229+
<details>
230+
<summary><strong>TimeSeries — LookBack</strong></summary>
231+
232+
| Block | Description |
233+
|---|---|
234+
| `lookBack1Opt ()` | Previous DataPoint as `voption` (ValueNone on first tick) |
235+
| `lookBack1 default` | Previous DataPoint, padded with default |
236+
| `lookBack2Opt ()` / `lookBack3Opt ()` | Previous 2/3 DataPoints as voption tuple |
237+
| `lookBack2 default` / `lookBack3 default` | Previous 2/3 DataPoints, padded with default |
238+
| `lookBackOpt n` | Previous N DataPoints as `voption list` |
239+
| `lookBack n default` | Previous N DataPoints as `list`, padded with default |
240+
241+
</details>
242+
243+
<details>
244+
<summary><strong>TimeSeries — LookAhead</strong></summary>
245+
246+
| Block | Description |
247+
|---|---|
248+
| `lookAhead1Opt ()` | (current, next) as `voption` — delays 1 tick |
249+
| `lookAhead1 default` | (current, next) — uses default before buffer fills |
250+
| `lookAhead2Opt ()` / `lookAhead3Opt ()` | (current, next1, next2/3) as voption — delays 2/3 ticks |
251+
| `lookAhead2 default` / `lookAhead3 default` | (current, next1, next2/3) padded with default |
252+
| `lookAheadOpt n` | (current, ahead list) as `voption` — delays N ticks |
253+
| `lookAhead n default` | (current, ahead list) — padded with default |
254+
255+
</details>
256+
257+
<details>
258+
<summary><strong>TimeSeries — Window Aggregation</strong></summary>
259+
260+
| Block | Description |
261+
|---|---|
262+
| `window n` | Sliding window of N DataPoints (raw buffer) |
263+
| `windowSum n` | Sum of values in sliding window. Input: `DataPoint<float>` |
264+
| `windowAvg n` | Average of values in sliding window |
265+
| `windowMin n` | Minimum value in sliding window |
266+
| `windowMax n` | Maximum value in sliding window |
267+
| `windowCount n` | Count of values in window (saturates at N) |
268+
| `cumulativeSum ()` | Running total over all values |
269+
| `intervalSum ()` | Cumulative sum that resets on boundary. Input: `(DataPoint<float> * bool)` |
270+
271+
</details>
272+
273+
<details>
274+
<summary><strong>TimeSeries — Interpolation</strong></summary>
275+
276+
| Block | Description |
277+
|---|---|
278+
| `sampleAndHold ()` | Hold last known value at target timestamp. Input: `(DateTimeOffset * DataPoint<'v> voption)` |
279+
| `nearestNeighbor ()` | Pick closest point by timestamp. Input: `(DateTimeOffset * DataPoint<float> voption)` |
280+
| `linear ()` | Linear interpolation between surrounding points. Input: `(DateTimeOffset * DataPoint<float> voption)` |
281+
282+
</details>
283+
284+
<details>
285+
<summary><strong>TimeSeries — Standalone Resamplers</strong></summary>
286+
287+
| Block | Description |
288+
|---|---|
289+
| `Aggregate.last` / `first` / `count` | Last/first value, count in window |
290+
| `Aggregate.sum` / `avg` / `min` / `max` | Numeric aggregations over `ResampleContext` |
291+
| `Interpolate.sampleAndHold` | Hold last known value |
292+
| `Interpolate.nearestNeighbor` | Pick nearest value by timestamp |
293+
| `Interpolate.linear` | Linear interpolation (float only) |
294+
295+
</details>
296+
215297
<details>
216298
<summary><strong>Operators</strong></summary>
217299

src/LocSta2/Core.fs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,17 @@ let ofSeq (sequence: seq<_>) : SigStream<_,_,_> =
6464
fun s ctx ->
6565
let enumerator =
6666
match s.Value with
67-
| ValueNone -> sequence.GetEnumerator()
67+
| ValueNone ->
68+
let enumerator = sequence.GetEnumerator()
69+
s.Set(enumerator)
70+
enumerator
6871
| ValueSome e -> e
6972
match enumerator.MoveNext() with
7073
| true ->
71-
s.Set(enumerator)
7274
Seq.singleton enumerator.Current, s
7375
| false ->
74-
failwith "Sequence contains no more elements"
76+
// failwith "Sequence contains no more elements"
77+
Seq.empty, s
7578

7679
/// Map over a stream
7780
let inline map ([<InlineIfLambda>] proj) ([<InlineIfLambda>] s1) =
@@ -124,21 +127,37 @@ let useState value =
124127
useMemo (MutableValue(value))
125128

126129
module Eval =
127-
/// Convert stream to sequence with context generator (flattens multi-value emissions)
128-
let inline toSeq
130+
/// Convert stream to sequence (flattens multi-value emissions).
131+
/// Stops after maxSilentTicks consecutive ticks that produce no values.
132+
let inline toSeqWith
133+
maxSilentTicks
129134
([<InlineIfLambda>] getCtx: int -> _)
130135
([<InlineIfLambda>] stream: SigStream<_,_,_>)
131136
=
132137
let state = StateController(ValueNone)
133138
seq {
134139
let mutable i = 0
135-
while true do
140+
let mutable silent = 0
141+
while silent < maxSilentTicks do
136142
let ctx = getCtx i
137143
i <- i + 1
138144
let vs, _ = stream state ctx
139-
yield! vs
145+
let mutable hadValue = false
146+
for v in vs do
147+
hadValue <- true
148+
yield v
149+
if hadValue then silent <- 0
150+
else silent <- silent + 1
140151
}
141152

153+
/// Convert stream to sequence (flattens multi-value emissions).
154+
/// Stops after 1000 consecutive silent ticks.
155+
let inline toSeq
156+
([<InlineIfLambda>] getCtx: int -> _)
157+
([<InlineIfLambda>] stream: SigStream<_,_,_>)
158+
=
159+
toSeqWith 1000 getCtx stream
160+
142161
/// Evaluate n samples
143162
let inline run n getCtx stream =
144163
toSeq getCtx stream |> Seq.take n |> Seq.toList
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project Sdk="Microsoft.NET.Sdk">
3+
<!--This is a temporary file used by Fable to restore dependencies.
4+
If you see this file in your project, you can delete it safely-->
5+
<PropertyGroup>
6+
<TargetFramework>net10.0</TargetFramework>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<Compile Include="Core.fs" />
10+
<Compile Include="TimeSeries/TimeSeries.fs" />
11+
<Compile Include="TimeSeries/LookBack.fs" />
12+
<Compile Include="TimeSeries/LookAhead.fs" />
13+
<Compile Include="TimeSeries/WindowAggregate.fs" />
14+
<Compile Include="TimeSeries/StreamInterpolate.fs" />
15+
<!-- Generators -->
16+
<Compile Include="Generators/Counter.fs" />
17+
<Compile Include="Generators/Fibonacci.fs" />
18+
<!-- Delay -->
19+
<Compile Include="Delay/DelayByN.fs" />
20+
<Compile Include="Delay/DelayBy1.fs" />
21+
<!-- State -->
22+
<Compile Include="State/Hold.fs" />
23+
<Compile Include="State/Latch.fs" />
24+
<Compile Include="State/Edge.fs" />
25+
<Compile Include="State/Changed.fs" />
26+
<!-- Arithmetic -->
27+
<Compile Include="Arithmetic/Diff.fs" />
28+
<Compile Include="Arithmetic/CumulativeSum.fs" />
29+
<Compile Include="Arithmetic/CumulativeProduct.fs" />
30+
<Compile Include="Arithmetic/Clamp.fs" />
31+
<Compile Include="Arithmetic/Rescale.fs" />
32+
<!-- Statistics -->
33+
<Compile Include="Statistics/MovingAverage.fs" />
34+
<Compile Include="Statistics/Ema.fs" />
35+
<Compile Include="Statistics/RollingStdDev.fs" />
36+
<Compile Include="Statistics/RollingMin.fs" />
37+
<Compile Include="Statistics/RollingMax.fs" />
38+
<Compile Include="Statistics/RunningMin.fs" />
39+
<Compile Include="Statistics/RunningMax.fs" />
40+
<!-- Detection -->
41+
<Compile Include="Detection/Crossover.fs" />
42+
<Compile Include="Detection/Threshold.fs" />
43+
<!-- Logic -->
44+
<Compile Include="Logic/Debounce.fs" />
45+
<Compile Include="Logic/Toggle.fs" />
46+
<!-- Counting -->
47+
<Compile Include="Counting/CountWhere.fs" />
48+
<Compile Include="Counting/CountSince.fs" />
49+
<Compile Include="Counting/CountIn.fs" />
50+
<Compile Include="Counting/TimeSince.fs" />
51+
<Compile Include="Counting/Rate.fs" />
52+
<!-- Windowing -->
53+
<Compile Include="Windowing/WindowedReduce.fs" />
54+
<Compile Include="Windowing/Segment.fs" />
55+
<!-- Operators -->
56+
<Compile Include="Operators/Operators.fs" />
57+
<!-- DSP: Oscillators -->
58+
<Compile Include="Dsp/Oscillators/SineOsc.fs" />
59+
<Compile Include="Dsp/Oscillators/SawOsc.fs" />
60+
<Compile Include="Dsp/Oscillators/SquareOsc.fs" />
61+
<Compile Include="Dsp/Oscillators/TriangleOsc.fs" />
62+
<Compile Include="Dsp/Oscillators/WhiteNoise.fs" />
63+
<!-- DSP: Filters -->
64+
<Compile Include="Dsp/Filters/LowPass1.fs" />
65+
<Compile Include="Dsp/Filters/HighPass1.fs" />
66+
<Compile Include="Dsp/Filters/BiquadLowPass.fs" />
67+
<Compile Include="Dsp/Filters/BiquadHighPass.fs" />
68+
<Compile Include="Dsp/Filters/BiquadBandPass.fs" />
69+
<Compile Include="Dsp/Filters/DcBlock.fs" />
70+
<!-- DSP: Envelope -->
71+
<Compile Include="Dsp/Envelope/EnvFollow.fs" />
72+
<Compile Include="Dsp/Envelope/Adsr.fs" />
73+
<!-- DSP: Dynamics -->
74+
<Compile Include="Dsp/Dynamics/SoftClip.fs" />
75+
<Compile Include="Dsp/Dynamics/HardClip.fs" />
76+
<Compile Include="Dsp/Dynamics/Gate.fs" />
77+
<!-- DSP: Modulation -->
78+
<Compile Include="Dsp/Modulation/RingMod.fs" />
79+
<Compile Include="Dsp/Modulation/BitCrush.fs" />
80+
<Compile Include="Dsp/Modulation/Crossfade.fs" />
81+
<!-- DSP: Analysis -->
82+
<Compile Include="Dsp/Analysis/Rms.fs" />
83+
<Compile Include="Dsp/Analysis/ZeroCrossRate.fs" />
84+
<Compile Include="Dsp/Analysis/PeakHold.fs" />
85+
</ItemGroup>
86+
</Project>

src/LocSta2/LocSta2.fsproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66

77
<ItemGroup>
88
<Compile Include="Core.fs" />
9-
<Compile Include="TimeSeries.fs" />
9+
<Compile Include="TimeSeries/TimeSeries.fs" />
10+
<Compile Include="TimeSeries/LookBack.fs" />
11+
<Compile Include="TimeSeries/LookAhead.fs" />
12+
<Compile Include="TimeSeries/WindowAggregate.fs" />
13+
<Compile Include="TimeSeries/StreamInterpolate.fs" />
1014
<!-- Generators -->
1115
<Compile Include="Generators/Counter.fs" />
1216
<Compile Include="Generators/Fibonacci.fs" />

0 commit comments

Comments
 (0)