39 lines
2.2 KiB
Markdown
39 lines
2.2 KiB
Markdown
# Arbitrary Number of Channels
|
||
|
||
## Overview
|
||
|
||
Originally the looper had a fixed maximum of 16 channels (`MAX_CHANNELS = 16`).
|
||
The limitation has been removed; channels are now stored in a **dynamically allocated array** that grows on demand.
|
||
|
||
## Implementation
|
||
|
||
- The global `channels` is a pointer (`struct channel_t *_Atomic channels`) instead of a fixed‑size array.
|
||
- An atomic variable `channel_capacity` tracks the allocated size.
|
||
- Initial allocation is for 8 channels; when a channel index >= current capacity is needed, the array is doubled.
|
||
- The old array is **not freed immediately** – it is kept alive for at least one real‑time audio cycle (using the same deferred mechanism as port unregistration) to guarantee that the RT callback never accesses freed memory.
|
||
|
||
## Key Files
|
||
|
||
| File | Role |
|
||
|--------------------|-----------------------------------------------------------|
|
||
| `src/channel.h` | Removes `MAX_CHANNELS`, adds `channels` pointer declaration and `get_channels_array()` inline accessor. |
|
||
| `src/looper.c` | Contains `ensure_capacity()`, deferred free, and replaces all fixed‑size loop bounds with `channel_capacity`. |
|
||
| `src/channel.c` | Adapted to use the current array pointer atomically. |
|
||
| `src/midi.c` | Uses `atomic_load(&channel_capacity)` for bounds checks. |
|
||
|
||
## Thread Safety During Resize
|
||
|
||
1. A new, larger array is allocated (`calloc`).
|
||
2. Existing channels are copied via `memcpy`.
|
||
3. The global `channels` pointer is swapped with `atomic_exchange`.
|
||
4. `channel_capacity` is updated.
|
||
5. The old pointer is stored in `pending_old` along with the current cycle count (`pending_old_cycle`).
|
||
6. In the main loop, `pending_old` is freed only after `global_rt_cycles` has advanced by at least 1, ensuring any RT callback that loaded the old pointer has finished.
|
||
|
||
This is a lightweight RCU‑like pattern that avoids locks and keeps the RT path deterministic.
|
||
|
||
## Compatibility
|
||
|
||
All existing MIDI commands and FIFO pipe commands work unchanged with the dynamic array.
|
||
The maximum practical number of channels is limited only by available memory and JACK port limits (typically 1024 per client on modern systems).
|