nCine/Audio/AudioMixerCommon.h file

Namespaces

namespace nCine
Root namespace of nCine game engine.
namespace nCine::AudioMixer

Functions

auto Clamp01(float value) -> float
auto ClampToInt16(std::int32_t value) -> std::int16_t
Saturates a mixed sample into the 16 bits every software backend hands its hardware.
auto ComputeResampleStep(std::int32_t sourceFrequency, std::int32_t mixFrequency, float pitch) -> std::int64_t
Per-output-frame step through a source's samples, as a 32.32 fixed-point cursor.
void ComputeStereoGains(bool relative, const Vector3f& position, const Vector3f& listenerPosition, float sourceGain, float masterGain, float& leftGain, float& rightGain)
Per-voice stereo gains of a software-mixing backend.

Function documentation

std::int16_t ClampToInt16(std::int32_t value)

Saturates a mixed sample into the 16 bits every software backend hands its hardware.

The accumulator a software mixer sums voices into is wider than the output on purpose, because with a dozen voices a loud moment genuinely exceeds the range. Wrapping the excess would invert the waveform's polarity, which is a far worse artefact than the clipping this does instead.

Shared so that a change to the saturation policy - a soft knee, dither, a bit of headroom - is one edit rather than one per backend, which is how the mixers have drifted before.

std::int64_t ComputeResampleStep(std::int32_t sourceFrequency, std::int32_t mixFrequency, float pitch)

Per-output-frame step through a source's samples, as a 32.32 fixed-point cursor.

Parameters
sourceFrequency Sample rate of the buffer being read
mixFrequency Sample rate the backend is mixing at
pitch Voice pitch, 1.0 for none

How far the read cursor advances for each frame the mixer writes: the ratio of the source's own rate to the rate being mixed at, scaled by the voice's pitch. The 32.32 form is what lets the inner loop advance with an integer add and take the interpolation fraction straight out of the low word.

Every software backend resamples this way, so it lives here rather than being spelled out (with its 4294967296.0) in each of them.

void ComputeStereoGains(bool relative, const Vector3f& position, const Vector3f& listenerPosition, float sourceGain, float masterGain, float& leftGain, float& rightGain)

Per-voice stereo gains of a software-mixing backend.

Shared by every backend that mixes on the CPU (PS3, N64, Amiga, SDL, PSP), so the positional model sounds the same on all of them - and the same as the OpenAL backend, whose distance model this is: AL_LINEAR_DISTANCE_CLAMPED with a rolloff factor of 1 between IAudioDevice::ReferenceDistance and IAudioDevice::MaxDistance, with constant-power panning across the X axis (the only one a 2D game's stereo field uses).

The frames matter. A player hands its backend a position in the OpenAL convention (see IAudioPlayer::getAdjustedPosition()): physical units, i.e. pixels times IAudioDevice::LengthToPhysical with Y and Z negated, and for a positional source in WORLD space

  • the listener was subtracted for the near-field smoothing and added back. The listener itself arrives through updateListener() in raw pixels, so it is brought into the same frame here before the difference is taken. A relative source (UI sounds, music, the 2D panning vector of length 1) is already head-locked and is used as it is. Getting this wrong is not subtle: with the listener left in pixels every positional sound went silent as soon as the camera was a few hundred pixels from the level's origin.