2.2 KiB
2.2 KiB
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
channelsis a pointer (struct channel_t *_Atomic channels) instead of a fixed‑size array. - An atomic variable
channel_capacitytracks 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
- A new, larger array is allocated (
calloc). - Existing channels are copied via
memcpy. - The global
channelspointer is swapped withatomic_exchange. channel_capacityis updated.- The old pointer is stored in
pending_oldalong with the current cycle count (pending_old_cycle). - In the main loop,
pending_oldis freed only afterglobal_rt_cycleshas 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).