# Audio Adapter

The `AdapterConfig` controls how transforms and sinks buffer and slice incoming
data before processing. It handles overlap (context around each chunk), stride
(output chunk size), gap handling, and offset alignment.

Most elements don't need an adapter — it's only required when processing
requires context beyond a single buffer (e.g., filtering, resampling) or when
output must be chunked to a specific size.

## AdapterConfig Parameters

| Parameter           | Default        | Description                                              |
|---------------------|----------------|----------------------------------------------------------|
| `overlap`           | `(0, 0)`       | `(before, after)` padding in offset units                |
| `stride`            | `0`            | Output chunk size in offsets (`0` = all available data)   |
| `pad_zeros_startup` | `False`        | Zero-pad the beginning instead of waiting for enough data |
| `skip_gaps`         | `False`        | Produce a full gap buffer if any gaps appear in the chunk |
| `backend`           | `NumpyBackend` | Array backend for buffer operations                      |
| `align_to`          | `None`         | Snap output offsets to multiples of this value            |
| `align_buffers`     | `False`        | Align slices to the minimum sample rate across pads      |
| `offset_shift`      | `0`            | Shift output offsets (for latency compensation)          |
| `enable`            | `None`         | Force enable/disable (`None` = auto-detect)              |

## Common Patterns

The two most frequently used parameters are `overlap` and `stride`. In
practice, most elements fall into one of three patterns.

### Overlap Only (Filtering/Context)

Use overlap when processing needs context around each chunk but doesn't need
to control the output size. The `overlap` tuple specifies `(before, after)`
padding in offset units:

```{.python notest}
from sgnts.base import AdapterConfig, Offset

# A filter that needs 127 samples of history before each chunk
config = AdapterConfig(
    overlap=(Offset.fromsamples(127, rate=2048), 0),
)
```

Used by: `Correlate`, `Resampler`.

### Stride Only (Chunking)

Use stride to control the output chunk size. This is useful for sinks that
need fixed-size blocks or transforms that operate on specific window lengths:

```{.python notest}
from sgnts.base import AdapterConfig, Offset

# Produce 1-second output chunks
config = AdapterConfig(
    stride=Offset.fromsec(1),
)
```

Used by: `DumpSeriesSink` (via downstream sinks), `TDCF`, `Converter`.

### Overlap + Stride (Windowed Processing)

Combine both when processing needs both context and fixed output size. Common
in spectral analysis with overlapping windows:

```{.python notest}
from sgnts.base import AdapterConfig, Offset

# 50% overlap with fixed output stride
config = AdapterConfig(
    overlap=(0, Offset.fromsamples(512, rate=2048)),
    stride=Offset.fromsamples(512, rate=2048),
)
```

## Offset Alignment

Use `align_to` to snap output offsets to regular boundaries. This is useful for
sinks that write data aligned to integer seconds or specific block sizes:

```{.python notest}
from sgnts.base import AdapterConfig, Offset

# Align output to integer-second boundaries
config = AdapterConfig(
    stride=Offset.fromsec(1),
    align_to=Offset.fromsec(1),
)
```

## Gap Handling

By default, gaps in the input are passed through to processing. Set
`skip_gaps=True` to produce a full gap buffer whenever any gap appears in the
current chunk — useful for algorithms that cannot handle partial gaps:

```{.python notest}
config = AdapterConfig(
    overlap=(0, Offset.fromsamples(256, rate=2048)),
    stride=Offset.fromsamples(256, rate=2048),
    skip_gaps=True,
)
```

## Startup Padding

When overlap is set, the adapter normally waits until enough data has
accumulated to fill the overlap region. Set `pad_zeros_startup=True` to
zero-pad the beginning instead, producing output from the very first buffer:

```{.python notest}
config = AdapterConfig(
    overlap=(Offset.fromsamples(64, rate=2048), Offset.fromsamples(64, rate=2048)),
    pad_zeros_startup=True,
)
```

## Latency Compensation

Filters that introduce a known latency can shift output offsets backward to
maintain correct timing:

```{.python notest}
from sgnts.base import AdapterConfig, Offset

# Compensate for 2-sample latency at 2048 Hz
config = AdapterConfig(
    overlap=(Offset.fromsamples(3, rate=2048), 0),
    offset_shift=-Offset.fromsamples(2, rate=2048),
)
```

## Using AdapterConfig in Elements

Elements that extend `TSTransform` or `TSSink` set `self.adapter_config` in
their `__post_init__()`:

```{.python notest}
from dataclasses import dataclass
from sgnts.base import AdapterConfig, Offset, TSTransform

@dataclass
class MyFilter(TSTransform):
    kernel_size: int = 128

    def __post_init__(self):
        super().__post_init__()
        self.adapter_config = AdapterConfig(
            overlap=(Offset.fromsamples(self.kernel_size - 1, rate=2048), 0),
        )
```
