Utilities#
utils/misc.hpp contains the engine’s random number functions
(Random, RandomF, BigRandom). They use a per-thread xorshift128+
generator and are safe on any thread, the audio thread included. Without a
seed the sequence is the same on every run. Call Randomize() once at
startup for a time-based seed, or RandomSeed(n) for a fixed one. A host
that links libYSE as a shared library seeds the engine’s generator through
the C API (yse_randomize, yse_random_seed), because these inline
functions would seed only the host’s own copy. See
Sessions and devices for starting the engine itself.
-
namespace YSE
Public API of libYSE — sound playback, mixing, and 3D positional audio.
Entry points:
YSE::System(lifecycle and audio device),YSE::Listener(3D origin),YSE::sound(a playable source),YSE::channel(mixing tree),YSE::reverb(positioned reverb zone),YSE::patcher(modular DSP graph),YSE::player(note sequencer). Sub-namespaces group domain-specific types:YSE::DSPfor signal processing,YSE::MIDIfor MIDI I/O,YSE::MUSICfor note / chord / motif primitives.Note
Apart from their constructors, the oscillator and
vcfclasses must only be invoked from inside a DSP callback /processbody.Functions
-
template<typename T0, typename T1, typename T2>
inline void Clamp(T0 &x, T1 min, T2 max)# Clamp
xto the inclusive range [min,max].Templated so it works on any types that compare with
<. The result is written back intox.
-
inline void Randomize()#
Seed the random generator from the current time.
Call once at startup.
Optional — without it the engine still produces random-looking output, only reproducibly so. This publishes a clock-derived seed base; every thread, the calling one included, re-seeds from it on its next draw.
Not real-time safe (it reads the system clock): call it from the control thread at startup, never from an audio callback. Note that the generator state is header-local, so a host that links the engine as a shared library seeds its own copy, not the engine’s — such a host calls the C API’s
yse_randomize(), which runs inside the library (issue #908).
-
inline void RandomSeed(UInt seed)#
Seed the random generator with a fixed value, for reproducible runs.
After
RandomSeed(s)the n-th thread to draw gets the same sequence every timesis published — in particular a thread that is the only one drawing (the audio thread running a patch) replays exactly. Every thread re-seeds on its next draw. Lock- and allocation-free. The same shared-library caveat asRandomize()applies: C hosts callyse_random_seed().
-
inline Int Random(Int max)#
Random integer in [0,
max).Returns 0 when
max <= 0.The empty range has no other sensible answer, and saying so explicitly is what retires the divide-by-zero that
rand() % maxcarried: the patcher’sgRandomobject lets a patch set its range to 0 from the audio thread.
Variables
-
namespace RANDOM#
Internals of the engine-wide pseudo-random generator.
The generator is xorshift128+ over per-thread state, replacing the C
rand()/srand()pair.rand()keeps hidden global state, so the concurrent calls this engine makes from the audio callback (thegRandompatcher object, granulator jitter, the LFO’s random shapes), from the control thread and from the manager threads are a data race on that state — and some libc implementations serialise insiderand(), which is exactly what must not happen on the callback path.Everything here is real-time safe: no allocation, no locks, no I/O, no division. The per-thread state is
thread_localand constant-initialised, so no lazy-init guard variable is checked on the hot path. A thread seeds itself on its first draw, which costs one well-predicted branch, one relaxed 32-bitfetch_addand two integer mixing rounds — once per thread, ever.Functions
-
inline void SeedThread()#
Give the calling thread a fresh, non-zero state.
Lock-free and allocation-free, so it is safe even when it happens to run inside an audio callback — which is where the first draw on the audio thread lands unless the host calls it earlier.
-
inline UInt Bounded(UInt bound)#
Uniform integer in [0,
bound) via Lemire’s multiply-shift reduction.Returns 0 when
boundis 0, so there is no division and no divide-by-zero to guard against.The rejection step of Lemire’s exactly unbiased variant is deliberately left out: it makes the running time unbounded, which does not belong in an audio callback. Without it the bias stays below
bound / 2^32— orders of magnitude under one least-significant bit for every bound this engine uses (grain offsets, comb tunings, note pitches, patcher ranges).
-
inline Flt NextFloat()#
Uniform float in [0, 1).
Draws 24 bits — the full float mantissa — against the 15 bits
RAND_MAXallowed on MSVC.
-
inline void Reseed(UInt base)#
Publish a new seed base and make every thread re-seed from it.
Resets the stream counter too, so the n-th thread to draw after the call gets the same stream every time the same
baseis published. Lock- and allocation-free; the re-seed itself happens lazily on each thread’s next draw.
Variables
-
std::atomic<UInt> SeedBase = {0x9E3779B9U}#
Global seed base, published by
Randomize().Left at a fixed constant otherwise, so an application that never calls
Randomize()gets a reproducible sequence — the same contract an unseededrand()offered.
-
std::atomic<UInt> StreamCounter = {0}#
Hands every thread a distinct stream index on its first draw.
-
std::atomic<UInt> SeedEpoch = {0}#
Bumped by every re-seed (
Randomize(),Reseed()).A thread whose
Epochno longer matches re-seeds on its next draw, which is how a seed published from the control thread reaches the audio thread’s already-running stream (issue #908). The check is one relaxed 32-bit load per draw — a plain load on every target.
-
std::array<U64, 2> State = {}#
Per-thread generator state;
{0, 0}means “not seeded yet”.Constant-initialised on purpose: a dynamically initialised
thread_localwould add a guard-variable check to every single draw.std::arraykeeps that property — it is an aggregate, so{}is still a constant initializer and the state stays in.tbss.
-
inline void SeedThread()#
-
template<typename T0, typename T1, typename T2>
-
namespace YSE
Public API of libYSE — sound playback, mixing, and 3D positional audio.
Entry points:
YSE::System(lifecycle and audio device),YSE::Listener(3D origin),YSE::sound(a playable source),YSE::channel(mixing tree),YSE::reverb(positioned reverb zone),YSE::patcher(modular DSP graph),YSE::player(note sequencer). Sub-namespaces group domain-specific types:YSE::DSPfor signal processing,YSE::MIDIfor MIDI I/O,YSE::MUSICfor note / chord / motif primitives.Note
Apart from their constructors, the oscillator and
vcfclasses must only be invoked from inside a DSP callback /processbody.Functions
-
class Pos#
3D position vector — the spatial coordinate used throughout libYSE.
Used for
soundpositions, theListenerposition, andreverbzone centres. Components are plain floats — read or writex,y,zdirectly. Arithmetic and comparison operators behave component-wise.Public Functions
-
inline float length()#
Euclidean length of the vector.
-
inline Pos()#
Construct at the origin (0, 0, 0).
-
inline Pos(float r)#
Construct with every component set to
r.
-
inline Pos(float x, float y, float z)#
Construct with explicit components.
-
Pos(const aPos &v)#
Construct from an atomic position object.
Friends
-
inline float length()#
-
class Pos#
-
namespace YSE
Public API of libYSE — sound playback, mixing, and 3D positional audio.
Entry points:
YSE::System(lifecycle and audio device),YSE::Listener(3D origin),YSE::sound(a playable source),YSE::channel(mixing tree),YSE::reverb(positioned reverb zone),YSE::patcher(modular DSP graph),YSE::player(note sequencer). Sub-namespaces group domain-specific types:YSE::DSPfor signal processing,YSE::MIDIfor MIDI I/O,YSE::MUSICfor note / chord / motif primitives.Note
Apart from their constructors, the oscillator and
vcfclasses must only be invoked from inside a DSP callback /processbody.-
class linearInterpolator#
Block-rate linear interpolator driven by an external time delta.
Call
setto start interpolating toward a target overtimeseconds, then callupdate(timeDelta)once per frame andoperator()to read the current value.Public Functions
-
linearInterpolator()#
-
linearInterpolator &set(Flt target, Flt time = 0)#
Start a new ramp toward
targetovertimeseconds.
-
linearInterpolator &update(Flt timeDelta)#
Advance the interpolator by
timeDeltaseconds.
-
linearInterpolator()#
-
template<class TYPE>
class objectInterpolator# Probabilistic cross-fade between two objects.
Unlike
linearInterpolator, this template does NOT blend the objects themselves — it returns either the current or the target object on each call. AstimeLeftdecreases, the probability of returning the target rises until it reaches 1.0 attimeLeft == 0. Useful for non-blendable state (pointers, discrete enums, etc.).Public Functions
-
inline objectInterpolator()#
-
inline objectInterpolator<TYPE> &set(TYPE obj, Flt time = 0)#
Set a new target object.
TYPEmust be copy-constructible unless it is itself a pointer. Setting a target for the first time replaces the current value immediately; subsequent calls start a cross-fade.
-
inline objectInterpolator<TYPE> &update(Flt timeDelta)#
Advance by
timeDeltaseconds.
-
inline objectInterpolator()#
-
class linearInterpolator#
-
namespace YSE
Public API of libYSE — sound playback, mixing, and 3D positional audio.
Entry points:
YSE::System(lifecycle and audio device),YSE::Listener(3D origin),YSE::sound(a playable source),YSE::channel(mixing tree),YSE::reverb(positioned reverb zone),YSE::patcher(modular DSP graph),YSE::player(note sequencer). Sub-namespaces group domain-specific types:YSE::DSPfor signal processing,YSE::MIDIfor MIDI I/O,YSE::MUSICfor note / chord / motif primitives.Note
Apart from their constructors, the oscillator and
vcfclasses must only be invoked from inside a DSP callback /processbody.