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::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

Functions

template<typename T0, typename T1, typename T2>
inline void Clamp(T0 &x, T1 min, T2 max)#

Clamp x to the inclusive range [min, max].

Templated so it works on any types that compare with <. The result is written back into x.

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 time s is 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 as Randomize() applies: C hosts call yse_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() % max carried: the patcher’s gRandom object lets a patch set its range to 0 from the audio thread.

inline Int Random(Int min, Int max)#

Random integer in [min, max).

Returns min when max <= min.

inline Int BigRandom(Int max)#

Heavy-tailed random integer biased toward 0; produces fewer high values than Random.

Returns 0 when max <= 0.

inline Flt RandomF()#

Random float in [0, 1).

inline Flt RandomF(Flt max)#

Random float in [0, max).

inline Flt RandomF(Flt min, Flt max)#

Random float in [min, max).

inline Flt *Random(Flt *min, Flt *max)#

Random pointer in [min, max), stride one Flt.

Both pointers must address the same array. Returns min for an empty range.

Variables

const Flt Pi_6 = 0.52359878f#

PI/6 (30°).

const Flt Pi_4 = 0.78539816f#

PI/4 (45°).

const Flt Pi_3 = 1.04719755f#

PI/3 (60°).

const Flt Pi_2 = 1.57079633f#

PI/2 (90°).

const Flt Pi = 3.14159265f#

PI (180°).

const Flt Pi2 = 6.28318531f#

PI × 2 (360°).

const Flt ToDegrees = 57.29577951f#

Multiply radians by this to get degrees.

const Flt ToRadians = 0.017453293f#

Multiply degrees by this to get radians.

const Flt Sqrt2 = 1.4142135623730950f#

√2.

const Flt Sqrt3 = 1.7320508075688773f#

√3.

const Flt Sqrt2_2 = 0.7071067811865475f#

√2 / 2.

const Flt Sqrt3_3 = 0.5773502691896257f#

√3 / 3.

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 (the gRandom patcher 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 inside rand(), 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_local and 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-bit fetch_add and two integer mixing rounds — once per thread, ever.

Functions

inline U64 Mix(U64 z)#

SplitMix64 finalizer — turns a counter into well-distributed bits.

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 U64 Next()#

xorshift128+ — 64 pseudo-random bits in a handful of instructions.

inline UInt Bounded(UInt bound)#

Uniform integer in [0, bound) via Lemire’s multiply-shift reduction.

Returns 0 when bound is 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_MAX allowed 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 base is 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 unseeded rand() 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 Epoch no 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_local would add a guard-variable check to every single draw. std::array keeps that property — it is an aggregate, so {} is still a constant initializer and the state stays in .tbss.

UInt Epoch = {0}#

The SeedEpoch the calling thread’s State was seeded under.

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::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

Functions

inline Int Min(Int x, Int y)#

Minimum of two integers.

inline Int Min(UInt x, Int y)#
inline Int Min(Int x, UInt y)#
inline UInt Min(UInt x, UInt y)#
inline I64 Min(Int x, I64 y)#
inline Flt Min(Flt x, Flt y)#
inline Flt Min(Int x, Flt y)#
inline Flt Min(Flt x, Int y)#
inline Dbl Min(Dbl x, Dbl y)#
inline Int Max(Int x, Int y)#
inline UInt Max(UInt x, Int y)#
inline UInt Max(Int x, UInt y)#
inline UInt Max(UInt x, UInt y)#
inline I64 Max(Int x, I64 y)#
inline Flt Max(Flt x, Flt y)#
inline Flt Max(Int x, Flt y)#
inline Flt Max(Flt x, Int y)#
inline Dbl Max(Dbl x, Dbl y)#
inline Pos Min(const Pos &a, const Pos &b)#

Component-wise minimum of two positions.

inline Pos Avg(const Pos &a, const Pos &b)#

Midpoint of two positions.

inline float Dist(const Pos &a, const Pos &b)#

Euclidean distance between two positions.

inline Flt Dot(const Pos &a, const Pos &b)#

Dot product of two positions.

Variables

static Pos PosEmpty#

Convenience zero-vector instance.

class Pos#

3D position vector — the spatial coordinate used throughout libYSE.

Used for sound positions, the Listener position, and reverb zone centres. Components are plain floats — read or write x, y, z directly. Arithmetic and comparison operators behave component-wise.

Public Functions

inline Pos &zero()#

Set all components to zero.

inline Pos &set(float r)#

Set all components to r.

inline Pos &set(float x, float y, float z)#

Set all three components.

inline float length()#

Euclidean length of the vector.

inline std::string asText()#

Human-readable "X: x Y: y Z: z" representation.

inline Pos &operator+=(float r)#

Add a scalar to every component.

inline Pos &operator-=(float r)#

Subtract a scalar from every component.

inline Pos &operator*=(float r)#

Multiply every component by a scalar.

inline Pos &operator/=(float r)#

Divide every component by a scalar.

inline Pos &operator+=(const Pos &v)#

Component-wise add another vector.

inline Pos &operator-=(const Pos &v)#

Component-wise subtract another vector.

inline Pos &operator*=(const Pos &v)#

Component-wise multiply by another vector.

inline Pos &operator/=(const Pos &v)#

Component-wise divide by another vector.

inline bool operator==(const Pos &v) const#

Component-wise equality.

inline bool operator!=(const Pos &v) const#

Component-wise inequality.

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.

Public Members

float x#

Cartesian components.

float y#
float z#

Friends

inline friend Pos operator+(const Pos &v, float r)#
inline friend Pos operator-(const Pos &v, float r)#
inline friend Pos operator*(const Pos &v, float r)#
inline friend Pos operator/(const Pos &v, float r)#
inline friend Pos operator+(float r, const Pos &v)#
inline friend Pos operator-(float r, const Pos &v)#
inline friend Pos operator*(float r, const Pos &v)#
inline friend Pos operator/(float r, const Pos &v)#
inline friend Pos operator+(const Pos &a, const Pos &b)#
inline friend Pos operator-(const Pos &a, const Pos &b)#
inline friend Pos operator*(const Pos &a, const Pos &b)#
inline friend Pos operator/(const Pos &a, const Pos &b)#
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::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

class linearInterpolator#

Block-rate linear interpolator driven by an external time delta.

Call set to start interpolating toward a target over time seconds, then call update(timeDelta) once per frame and operator() to read the current value.

Public Functions

linearInterpolator()#
linearInterpolator &set(Flt target, Flt time = 0)#

Start a new ramp toward target over time seconds.

linearInterpolator &update(Flt timeDelta)#

Advance the interpolator by timeDelta seconds.

Flt target()#

Current target value.

Flt operator()()#

Current interpolated value.

Private Members

Flt startValue#
Flt targetValue#
Flt currentValue#
Flt totalTime#
Flt timeLeft#
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. As timeLeft decreases, the probability of returning the target rises until it reaches 1.0 at timeLeft == 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.

TYPE must 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 timeDelta seconds.

inline TYPE &operator()()#

Return either the current or target object, weighted by how far the cross-fade has progressed.

inline Bool isSet()#

Whether any object has been set.

Private Members

TYPE current#
TYPE target#
Flt totalTime#
Flt timeLeft#
Bool _isSet#
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::DSP for signal processing, YSE::MIDI for MIDI I/O, YSE::MUSIC for note / chord / motif primitives.

Note

Apart from their constructors, the oscillator and vcf classes must only be invoked from inside a DSP callback / process body.

Functions

std::string GetCurrentWorkingDirectory()#

Current working directory of the process.

bool FileExists(const std::string &name)#

Whether a file at name exists on disk.

bool IsPathAbsolute(const std::string &path)#

Whether path is absolute (vs.

relative to the working directory).