#include <freeze_hold.hpp>
Classes | |
| struct | Config |
| Two sizing modes. More... | |
Public Types | |
| using | Snapshot = FreezeHoldSnapshotT<SampleType> |
Public Member Functions | |
| void | prepare (const Config &requested) |
| RT contract: prepare() allocates capture/hold storage and is not audio-thread safe. | |
| const Config & | config () const |
| The resolved configuration this hold was prepared with. | |
| void | set_capture_frames (int frames) |
| Hold length: the number of most recent frames the NEXT latch averages, clamped to [2, max_capture_frames]. | |
| void | set_capture_seconds (double seconds) |
| Seconds-based form of set_capture_frames(); ignored unless the hold was prepared with a sample rate. | |
| int | capture_frames () const |
| The capture depth the next latch uses. | |
| void | reset () |
| Full reset: drops the hold and the capture history and re-seeds the PRNG (the prepared state). | |
| void | clear_history () |
| Forget captured input only. | |
| void | set_frozen (bool frozen) |
| RT-safe engage/release request. | |
| bool | is_engaged () const |
| The last engage/release request. | |
| bool | is_latched () const |
| True once held content is actually playing (capture was full). | |
| bool | is_releasing () const |
| True while a released hold is fading out. | |
| bool | has_hold () const |
| True once a hold has been latched or restored since the last reset(); the held-state accessors and write_hold() describe it. | |
| SampleType | engage_progress () const |
| Hold-weight position of the crossfade used for the most recent frame group: 0 = live only, 1 = hold only, stepping by 1/crossfade_frames per frame group. | |
| std::span< const SampleType > | held_magnitudes (int channel) const |
| Averaged magnitudes of one channel's hold (num_bins values). | |
| std::span< const double > | held_phases (int channel) const |
| Current hold phases of one channel, wrapped to [-pi, pi]: the phases the next hold frame is rendered with. | |
| std::span< const double > | instantaneous_frequency () const |
| Per-bin instantaneous frequency of the hold, radians per sample. | |
| bool | write_hold (std::complex< SampleType > *const *frames, int channels, int num_bins) const |
| Render the current hold frame (magnitude and phase only — no live mix, no crossfade, no phase advance) into frames. | |
| bool | engage_pending () const |
| True while latch- or restore-side rotor construction is still being staged across frame groups (engage_bins_per_hop > 0). | |
| void | advance_hold (int frames=1) |
| Advance the hold by frames analysis hops exactly as process_group() does after each held frame (instantaneous frequency plus the random walk, consuming the PRNG). | |
| void | rewind_hold_phases (int frames) |
| Move the hold phases back by frames hops at the instantaneous frequency, without the random walk and without touching the PRNG — e.g. | |
| bool | snapshot (Snapshot &out) const |
| Copy the current hold into out, which must have been prepared for this geometry. | |
| bool | stage_restore (const Snapshot &in, FreezeRestoreEngage engage=FreezeRestoreEngage::crossfade) |
| Stage in to become the hold at the start of the next process_group(). | |
| bool | restore_pending () const |
| True while a staged restore is waiting for the next frame group. | |
| void | process_group (std::complex< SampleType > *const *frames, int channels, int num_bins) |
| Run at the head of the vocoder chain for every analysis frame group. | |
Static Public Member Functions | |
| static Config | resolve (const Config &config) noexcept |
| Resolve the time-based fields of config into frame counts: the exact configuration prepare() runs with. | |
| static int | frames_for_seconds (double seconds, const Config &config) noexcept |
| round(seconds * sample_rate / analysis_hop) for a seconds-based config (0 when it is not seconds-based or seconds is not positive). | |
| static bool | checked_retained_bytes (const Config &requested, std::uint64_t target_max_bytes, std::uint64_t &bytes) noexcept |
| using pulp::signal::FreezeHoldT< SampleType >::Snapshot = FreezeHoldSnapshotT<SampleType> |
|
inline |
Advance the hold by frames analysis hops exactly as process_group() does after each held frame (instantaneous frequency plus the random walk, consuming the PRNG).
For consumers that render the hold with write_hold() instead of process_group(); never combine the two on the same frame.
|
inline |
The capture depth the next latch uses.
|
inlinestaticnoexcept |
|
inline |
Forget captured input only.
The hold, its request and fade position, and the PRNG are kept, so a playing hold continues seamlessly across a transport jump; an armed or later freeze re-fills its capture window from input analyzed after this call.
|
inline |
The resolved configuration this hold was prepared with.
|
inline |
True while latch- or restore-side rotor construction is still being staged across frame groups (engage_bins_per_hop > 0).
|
inline |
Hold-weight position of the crossfade used for the most recent frame group: 0 = live only, 1 = hold only, stepping by 1/crossfade_frames per frame group.
Engage mixes hold/live linearly at this position; release mixes them with equal-power gains sin/cos(progress * pi/2). 0 while not latched. engage_progress() == 1 && !is_releasing() means the last frame group was pure hold.
|
inlinestaticnoexcept |
round(seconds * sample_rate / analysis_hop) for a seconds-based config (0 when it is not seconds-based or seconds is not positive).
|
inline |
True once a hold has been latched or restored since the last reset(); the held-state accessors and write_hold() describe it.
|
inline |
Averaged magnitudes of one channel's hold (num_bins values).
|
inline |
Current hold phases of one channel, wrapped to [-pi, pi]: the phases the next hold frame is rendered with.
With rotor synthesis the phases are derived from the phasors on each call (one atan2 per bin): an inspection accessor, not a per-hop call.
|
inline |
Per-bin instantaneous frequency of the hold, radians per sample.
While engage_pending() the bins not yet staged are incomplete.
|
inline |
The last engage/release request.
|
inline |
True once held content is actually playing (capture was full).
Stays true through the release fade.
|
inline |
True while a released hold is fading out.
|
inline |
|
inline |
Run at the head of the vocoder chain for every analysis frame group.
Captures live frames; when engaged and latched, replaces frames with hold content (crossfaded at the edges).
|
inline |
Full reset: drops the hold and the capture history and re-seeds the PRNG (the prepared state).
For a transport discontinuity that should keep an engaged hold, use clear_history() instead. A restore staged by stage_restore() stays pending and is adopted by the next process_group(), so a host that resets after recalling state still hears the recalled hold.
|
inlinestaticnoexcept |
|
inline |
True while a staged restore is waiting for the next frame group.
Safe from any thread.
|
inline |
Move the hold phases back by frames hops at the instantaneous frequency, without the random walk and without touching the PRNG — e.g.
to align a hold with a path that runs a fixed number of hops behind it. Not an exact inverse of advance_hold(), whose walk it does not undo.
With rotor synthesis a rewind while engage_pending() first finishes the staged rotor construction, since it needs every bin's rotor.
|
inline |
Hold length: the number of most recent frames the NEXT latch averages, clamped to [2, max_capture_frames].
RT-safe; it does not change a hold already playing. Survives reset() and clear_history(). A shorter window freezes closer to "now"; a longer one blends more of the recent past into a smoother hold.
|
inline |
Seconds-based form of set_capture_frames(); ignored unless the hold was prepared with a sample rate.
|
inline |
RT-safe engage/release request.
Releasing a latched hold commits it to fade out and restarts the capture window, so the next freeze averages only frames analyzed from the release onward (re-arming, without muting, until the window is full again) — also when the freeze is requested again before the release fade has finished.
|
inline |
Copy the current hold into out, which must have been prepared for this geometry.
Returns false (leaving out unchanged) when there is no hold, the storage does not match, or engage staging has not finished (engage_pending()). Allocation-free.
|
inline |
Stage in to become the hold at the start of the next process_group().
Safe to call from ONE non-audio thread concurrently with process_group(); copies into storage allocated at prepare(). Returns false — the caller should drop the capture and re-arm — for a snapshot whose fft_size, channels or analysis_hop differ from this hold's, whose known sample rate differs from this hold's known rate, an invalid snapshot, or when an earlier restore has not been adopted yet. crossfade and immediate adoption engage the hold (set_frozen(true) is implied); as_captured resumes the recorded request and fade position.
|
inline |
Render the current hold frame (magnitude and phase only — no live mix, no crossfade, no phase advance) into frames.
Returns false, writing nothing, when there is no hold or the shape does not match.