# Transforms

All transforms are imported from `sgnts.transforms`.

## Amplify

Multiplies data by a constant factor.

```{.python notest}
from sgnts.transforms import Amplify

amp = Amplify(name="amp", factor=2.5)
```

Gaps pass through unchanged. One-to-one (one sink pad, one source pad).

## Adder

Sums data from multiple sink pads into a single output.

```{.python notest}
from sgnts.transforms import Adder

# Sum two inputs
add = Adder(name="add", sink_pad_names=["H1", "L1"])
```

When all inputs are gaps, the output is a gap. Many-to-one.

**Selective addition** with `addslices_map` — add only specific array slices
from each input:

```{.python notest}
add = Adder(
    name="add",
    sink_pad_names=["a", "b"],
    addslices_map={"a": (slice(0, 2),), "b": (slice(2, 4),)},
)
```

## Align

Synchronizes frames from multiple sink pads by offset. Each sink pad maps
directly to a source pad with the same name, ensuring time-aligned output.

```{.python notest}
from sgnts.transforms import Align

align = Align(name="align", sink_pad_names=["H1", "L1"], source_pad_names=["H1", "L1"])
```

## Correlate

Cross-correlates input data with a fixed filter using `scipy.signal.correlate`.

```{.python notest}
import numpy as np
from sgnts.transforms import Correlate

filt = np.array([1.0, -1.0, 1.0])
corr = Correlate(name="corr", sample_rate=2048, filters=filt)
```

Uses the audio adapter internally to manage overlap for valid-mode
correlation. One-to-one.

**Multi-dimensional filters** — filter shape `(N, L)` produces N output
channels from each input:

```{.python notest}
filters = np.random.randn(4, 128)  # 4 filters of length 128
corr = Correlate(name="corr", sample_rate=2048, filters=filters)
```

## AdaptiveCorrelate

Like `Correlate`, but accepts filter updates on a second sink pad during
pipeline execution.

```{.python notest}
from sgnts.transforms import AdaptiveCorrelate

acorr = AdaptiveCorrelate(
    name="acorr",
    sink_pad_names=["data", "filters"],
    source_pad_names=["out"],
    sample_rate=2048,
)
```

New filters arrive as `EventFrame` objects on the `"filters"` pad and blend
in using a cosine-squared window for smooth transitions.

## Converter

Converts data between array backends (NumPy/PyTorch) or data types
(float32/float16).

```{.python notest}
from sgnts.transforms import Converter

# Convert to float16
conv = Converter(name="conv", dtype="float16")
```

```{.python notest}
# Convert to PyTorch tensors
conv = Converter(name="conv", backend="torch")
```

One-to-one.

## Gate

Passes data only where a control signal is non-gap. Requires two sink pads:
one for data and one named as the control.

```{.python notest}
from sgnts.transforms import Gate

gate = Gate(
    name="gate",
    sink_pad_names=["data", "ctrl"],
    source_pad_names=["out"],
    control="ctrl",
)
```

Where the control pad has gaps, the data output becomes a gap. Where the
control is non-gap, data passes through. Two-to-one.

## Matmul

Applies matrix multiplication to each time step: `out = matrix @ data`.

```{.python notest}
import numpy as np
from sgnts.transforms import Matmul

# Reduce 4 channels to 2
matrix = np.random.randn(2, 4)
mm = Matmul(name="mm", matrix=matrix)
```

Input shape `(4, T)` with a `(2, 4)` matrix produces output shape `(2, T)`.
One-to-one.

## Resampler

Changes sample rate via windowed sinc interpolation.

```{.python notest}
from sgnts.transforms import Resampler

# Downsample from 4096 Hz to 2048 Hz
res = Resampler(name="res", inrate=4096, outrate=2048)
```

```{.python notest}
# Upsample from 2048 Hz to 8192 Hz
res = Resampler(name="res", inrate=2048, outrate=8192)
```

Uses the audio adapter for overlap management. Supports NumPy and PyTorch
backends. One-to-one.

## SumIndex

Sums array values over slices along the first (non-time) dimension.

```{.python notest}
from sgnts.transforms import SumIndex

# Sum channels 0-1 and 2-3 separately: (4, T) -> (2, T)
si = SumIndex(name="si", sl=[slice(0, 2), slice(2, 4)])
```

Each slice in the list produces one output channel. One-to-one.

## Threshold

Converts data below (or above, if `invert=True`) a threshold to gaps.

```{.python notest}
from sgnts.transforms import Threshold

# Keep only samples with absolute value above 0.5
th = Threshold(name="th", threshold=0.5)
```

**Window parameters** extend the non-gap region around threshold crossings:

```{.python notest}
# Keep 64 samples before and 128 samples after each crossing
th = Threshold(name="th", threshold=0.5, startwn=64, stopwn=128)
```

**Invert** to keep only data below the threshold:

```{.python notest}
th = Threshold(name="th", threshold=0.5, invert=True)
```

One-to-one.

## ANDTransform

Logical AND across multiple inputs: output is non-gap only where all inputs
are non-gap.

```{.python notest}
from sgnts.transforms import ANDTransform

andt = ANDTransform(name="and", sink_pad_names=["a", "b"])
```

Many-to-one.

## NaryTransform

N-ary reduction across inputs using a configurable operation.

```{.python notest}
from sgnts.transforms import NaryTransform

nary = NaryTransform(name="nary", sink_pad_names=["a", "b"], operation="sum")
```

Many-to-one.

## BitVector

Bit-level operations on integer data.

```{.python notest}
from sgnts.transforms import BitVector

bv = BitVector(name="bv")
```

One-to-one.
