Documentation

LevelAPI2 & LevelShow2
R128 Loudness C API Documentation

LevelAPI2 is a high-performance C dynamic library for loudness measurement and normalization to EBU R128 and ATSC A/85 (CALM Act) broadcast standards. This reference covers the full API, integration workflow, and all function signatures. The realtime adjuster, which holds a live signal on a loudness target in a single pass, has its own chapter. LevelShow2, the bundled DirectShow filter for Windows pipelines, is documented in the second half.

01 Introduction

LevelAPI2 and LevelShow2 are built to support the transition from peak normalization to loudness normalization, one of the most significant changes in broadcast audio in decades. This shift began when the ITU published BS.1770 (LKFS/LUFS loudness metering) in 2006. The European Broadcast Union built on this foundation with EBU R128, released in 2010, which became the global broadcast loudness standard.

LevelAPI2 is the direct evolution of the software used by the EBU PLOUD working group during the creation of R128. It is maintained by tlemon.com.

The library interface consists entirely of C functions exposed through a dynamic library (.dll on Windows, .so on Linux). This makes it callable from any language with standard C FFI support: C, C++, Java, LUA, Python, Rust, Go, .NET, Delphi, and others.

02 System Requirements

  • Windows 7 or later (x86 / x64)
  • Linux: built on CentOS 7 (x64); custom builds available for other distributions
  • System memory: 512 MB minimum
  • Disk space: approximately 10 MB

The Linux .so build depends on standard shared libraries: libcurl.so.4, librt.so.1, libdl.so.2, libpthread.so.0, libm.so.6, libc.so.6. All are present in any standard CentOS 7 / RHEL / Fedora environment.

03 Upgrading from LevelAPI v1

If you are migrating from the original LevelAPI, note the following breaking changes:

Renamed functions

v1v2
Analyse()AnalyseInterleaved()
Adjust()AdjustInterleaved()
GetNeedLimiting()GetLimitingNeeded()
GetLimitThresholddB()GetLimiterThreshold()
SetNeedLimiting()SetLimiterEnable()
SetLimiterThresholdEffective()SetLimiterThresholdAdjust()
SetLimitThresholddB()SetLimiterThreshold()
IsAdjustLevelLowered()Removed. Use GetLevel(AdjustLevel_NoPeak) vs GetLevel(AdjustLevel)

Renamed enum values

v1v2
Max_MLU_Max_M
Max_SLU_Max_S
Max_M_FSLUFS_Max_M
Max_S_FSLUFS_Max_S
LRA_LowLRA_Range_Low
LRA_HighLRA_Range_High
TruePeakHP / TruePeakLPTruePeak (high precision only)
PSR_MinPSR

Behaviour changes

  • All functions that return a boolean now return 1 (true) on success and 0 (false) on failure. Previously this was inconsistent.
  • PPM and RMS meters are disabled by default. Enable with SetPPMEnable(true) and SetRMSEnable(true).
  • TruePeak HP and LP have been merged into a single high-precision TruePeak.

04 Usage Guide

Implementing LevelAPI2 follows a consistent pattern regardless of language or platform. The steps below describe the complete workflow from loading the library to retrieving results.

  1. Load the dynamic library at runtime

    Use your platform's dynamic linking API (dlopen on Linux, LoadLibrary on Windows) to load the library file. A helper macro LevelAPI2_LoadDLL(path, &handles) is provided in the header to retrieve all function pointers in one call.

  2. Validate your certificate

    Call SetCertificate(cert_string, length) as the first call after loading. This validates your license. If it returns false, call GetErrorString() to retrieve the failure reason. The certificate string should be embedded or loaded from a secure location in your application.

  3. Create a handle

    Call Create_LevelAPI2(channels, samplerate, bitdepth) to create an instance. Every subsequent function call requires this handle as its first argument. You can create multiple handles to measure multiple sources simultaneously. Always call Destroy_LevelAPI2(handle) when done.

  4. Configure measurement standard

    For EBU R128 compliance, call SetPresetEBU_R128(handle). For ATSC A/85, call SetPresetATSC(handle). These convenience functions set calibration, relative gate, dialog gate, target type and level in one call. You can then override individual settings using the Advanced Settings functions if needed.

  5. Feed audio samples

    Call AnalyseInterleaved(handle, samples, numframes) with your interleaved float buffer. The numframes argument is in sample frames: for a stereo buffer of 512 samples, numframes = 256. For best performance, pass blocks of at least 256 frames. There is no maximum: a whole file in one call measures the same as the same file in blocks. For file-based workflows, use AnalyseFile(handle, filepath) directly.

  6. Read meter levels

    At any point after analysis, call GetLevel(handle, meterType, channel) using values from the MeterTypes enum. Pass -1 as channel to get the combined result across all channels. Key values: LU, LRA, TruePeak, LU_Max_M, LU_Max_S.

  7. Normalize the audio

    After analysis is complete, call AdjustInterleaved(handle, samples, numframes) to apply gain correction to your sample buffer. If using the same handle for both steps, the required adjustment level is already known. If using a separate handle for adjustment, first call SetAdjustLevel(handle, dB) with the value from GetAdjustLevel() or GetLevel(AdjustLevel). The built-in peak limiter activates automatically when needed.

Minimal C example

C
#include "LevelAPI2_dynlib.h"

// 1. Load library
struct LevelAPI2_FunctionHandles api;
void* lib = LevelAPI2_LoadDLL(library_path, &api);

// 2. Validate certificate
api.SetCertificate(cert_string, strlen(cert_string));

// 3. Create handle
LevelAPI2Ptr h = api.Create_LevelAPI2(channels, samplerate, bitdepth);

// 4. Configure for EBU R128
api.SetPresetEBU_R128(h);

// 5. Feed samples (interleaved float buffer)
//    numframes = total_samples / channels
api.AnalyseInterleaved(h, samples, numframes);

// 6. Read results
float lu       = api.GetLevel(h, LU,       -1);
float lra      = api.GetLevel(h, LRA,      -1);
float truepeak = api.GetLevel(h, TruePeak, -1);

// 7. Normalize
api.AdjustInterleaved(h, samples, numframes);

// Cleanup
api.Destroy_LevelAPI2(h);
LevelAPI2_UnloadDLL(lib);

05 Constants & Enums

DataRanges

ConstantValueDescription
max_channels16Maximum number of audio channels
max_frames16384Not a limit. Every Analyse, Adjust and realtime call takes any number of frames; the constant is kept so existing code still compiles

SurroundOrders

ValueChannel Order
SMTPE_ITU_AC3L, R, C, Lfe, Ls, Rs
FilmDolbyDigitalL, C, R, Ls, Rs, Lfe
DTS_ProControlL, R, Ls, Rs, C, Lfe

LoudnessStandards

Used as the standard argument in SetPresetStandard(). Each value names one loudness standard, and one variant of it where a single document defines more than one recipe. The parameters each value applies are in section 11.

The numeric values are permanent: they are never renumbered and never reused, and each family has its own block of 100 so that one family can grow without moving any other value. A build that does not know a value returns false from SetPresetStandard() and changes nothing, so a version mismatch fails visibly instead of measuring to the wrong practice.

ConstantValueStandard
LEVELAPI2_STANDARD_NONE0No standard selected
LEVELAPI2_STANDARD_EBU_R128100EBU R 128
LEVELAPI2_STANDARD_EBU_R128_2010101EBU R 128, withdrawn 2010 edition
LEVELAPI2_STANDARD_EBU_R128_S2_STREAMING102EBU R 128 s2, streaming
LEVELAPI2_STANDARD_EBU_R128_S4_CINEMA103EBU R 128 s4, cinematic
LEVELAPI2_STANDARD_ATSC_A85_LONGFORM200ATSC A/85, long form
LEVELAPI2_STANDARD_ATSC_A85_SHORTFORM201ATSC A/85, short form
LEVELAPI2_STANDARD_ARIB_TRB32300ARIB TR-B32
LEVELAPI2_STANDARD_FREETV_OP59400Free TV Australia OP-59
LEVELAPI2_STANDARD_ITU_BS1770_1500ITU-R BS.1770-1, ungated
LEVELAPI2_STANDARD_ITU_BS1770_4501ITU-R BS.1770-4, gated

MeterTypes

Used as the meterType argument in GetLevel() and related functions. Values marked (*) are generally for testing only and not intended for display to end users.

ValueDescription
LUIntegrated loudness, calibrated LU (gated)
LUFSIntegrated loudness, uncalibrated LUFS (gated)
LU_UNGATEDCalibrated LU without gating (*)
LUFS_UNGATEDUncalibrated LUFS without gating (*)
LU_MinMinimum calibrated LU level (*)
LU_DialogCalibrated dialog LU level (*)
LUFS_DialogUncalibrated dialog LUFS level (*)
LU_Dialog_UNGATEDDialog LU without gating (*)
Dialog_percentagePercentage of dialog detected in the audio
LU_Max_MMaximum momentary level (calibrated LU)
LUFS_Max_MMaximum momentary level (uncalibrated LUFS)
LU_Max_SMaximum short-term level, 3 s window (calibrated LU)
LUFS_Max_SMaximum short-term level, 3 s window (uncalibrated LUFS)
LRALoudness range (LRA_High − LRA_Low)
LRA_Range_LowLow boundary of the LRA calculation (*)
LRA_Range_HighHigh boundary of the LRA calculation (*)
LU_BS1771Realtime calibrated LU (BS.1771) (*)
LU_BS1771_FilteredRealtime calibrated LU with meter filter
LU_BS1771_Shortterm3 s realtime calibrated LU
LUFS_BS1771_Shortterm3 s realtime uncalibrated LUFS (*)
PPMRealtime PPM level in dB, calibrated (must be enabled)
PPM_MaxMaximum PPM value in dB, calibrated
RMS_MaxMaximum RMS value in dBFS (must be enabled)
PeakMaximum sample peak (*)
TruePeakMaximum true inter-sample peak
PSRPeak-to-Short-term Ratio
PeakLoudnessPLR, Peak Loudness Range
Limited_dBAmount of limiting applied in dB (*)
AdjustLevelThe calculated normalization gain in dB
AdjustLevel_NoPeakNormalization gain ignoring peak limiting (*)

06 Dynlib Initialization

These functions are called immediately after loading the dynamic library, before creating any handles.

bool SetCertificate(const char *certificate_string, size_t bufferSize)

Must be the first call made to the loaded library. Validates your license certificate. Returns true on success.

  • arg 1Pointer to a char array containing the certificate text
  • arg 2Total byte length of the certificate text (max 10 × 1024)
bool GetErrorString(const char *error_string, size_t bufferSize)

Call only when SetCertificate() has returned false. Fills the provided buffer with the error description.

  • arg 1Pointer to an empty char array to receive the error text
  • arg 2Size of the buffer (max 10 × 1024)
LevelAPI2Ptr Create_LevelAPI2(const int channels, const unsigned int samplerate, const int bitdepth)

Creates and initializes a LevelAPI2 instance. Returns a valid handle, or NULL on failure. Every subsequent function call requires this handle as its first argument. Do not modify the returned value.

  • channelsNumber of audio channels, e.g. 2 for stereo
  • samplerateSample rate of the source material, e.g. 48000
  • bitdepthBit depth used for dithering during gain adjustment: 16, 24, or 32
void Destroy_LevelAPI2(LevelAPI2Ptr handle)

Releases all resources associated with the given handle. Must be called for every handle created with Create_LevelAPI2().

int GetRevision()

Returns the integer revision number of the loaded library binary. Useful for version checks at startup.

07 Library Initialization

bool SetPresetEBU_R128(LevelAPI2Ptr handle)

Convenience preset for full EBU R128 compliance. Equivalent to calling: SetCalibration(LU, −23), SetAdjustTargetType(LU), SetAdjustTargetLevel(0), SetRelativeGate(−10), SetDialogGate(false). Identical to SetPresetStandard(LEVELAPI2_STANDARD_EBU_R128), which is the newer form.

Returns true on success.
bool SetPresetATSC(LevelAPI2Ptr handle)

Convenience preset for ATSC A/85 (CALM Act) long-form content. Equivalent to: SetCalibration(LU, −24), SetAdjustTargetType(LU_Dialog), SetAdjustTargetLevel(0), SetRelativeGate(0), SetDialogGate(true). The dialogue gate A/85 requires is part of the preset: no second call is needed. Relative gating is off, which is what A/85 asks for on long-form content, and normalization moves the dialogue loudness onto the target rather than the full mix. Identical to SetPresetStandard(LEVELAPI2_STANDARD_ATSC_A85_LONGFORM).

Returns true on success.

You can combine presets with manual overrides. For example: call SetPresetEBU_R128() then SetCalibration() to override only the calibration value.

The following functions return stream format properties set during initialization:

unsigned int GetChannels(LevelAPI2Ptr)

Returns the number of channels.

unsigned int GetSamplerate(LevelAPI2Ptr)

Returns the sample rate.

08 Processing

bool AnalyseInterleaved(LevelAPI2Ptr handle, float* samples, const unsigned int numframes)

Analyses a block of interleaved float samples. Call repeatedly to process a complete audio stream. For best performance, pass blocks of at least 256 frames.

  • samplesPointer to an interleaved float buffer
  • numframesNumber of sample frames (= total samples ÷ channels). Any number, from one upward
Returns true on success.
bool Analyse(LevelAPI2Ptr handle, float* samples, const unsigned int numframes)

Non-interleaved variant. Use when your audio data is in separate channel buffers rather than interleaved.

bool AdjustInterleaved(LevelAPI2Ptr handle, float* samples, const unsigned int numframes)

Applies loudness normalization gain to the sample buffer in-place. Uses the AdjustLevel calculated by AnalyseInterleaved() (same handle), or the value set manually with SetAdjustLevel(). The peak limiter activates automatically when required.

  • samplesPointer to the float buffer to be modified in-place
  • numframesNumber of sample frames
bool Adjust(LevelAPI2Ptr handle, float* samples, const unsigned int numframes)

Non-interleaved variant. Use when your audio data is in separate channel buffers rather than interleaved.

bool AnalyseFile(LevelAPI2Ptr handle, const char* filepath)

Analyses an audio file directly. Supported formats: WAV, AIFF, FLAC.

bool AdjustFile(LevelAPI2Ptr handle, const char* infile, const char* outfile)

Normalizes an audio file and writes the result to a new file. The output path must not already exist.

  • infilePath to the source audio file (WAV / AIFF / FLAC)
  • outfilePath for the normalized output file (must not exist)

09 Results

float GetLevel(LevelAPI2Ptr handle, enum MeterTypes meterType, const int channel)

Returns a meter value in dB for the given meter type and channel.

  • meterTypeA value from the MeterTypes enum, e.g. LU, LRA, TruePeak
  • channelChannel index (0-based), or -1 for the combined result across all channels
float GetAdjustLevel(LevelAPI2Ptr handle, const bool ignore_peak_limit)

Returns the calculated normalization gain in dB. Use this to apply the gain manually, or save it for later use with a separate handle. Pass false (default) to include peak limiting in the calculation.

bool SetAdjustLevel(LevelAPI2Ptr handle, const float adjustLevel)

Manually sets the normalization gain in dB. Not needed when using the same handle for both analysis and adjustment. Use this when separating the analyse and adjust passes, for example to analyse multiple files first, then adjust them in a second pass.

unsigned int GetAdjustLatency(LevelAPI2Ptr handle)

Returns the latency in samples introduced by the peak limiter during adjustment (approximately 20 ms). Account for this when synchronizing audio streams.

10 Advanced Settings

These functions modify measurement behaviour beyond the standard presets. For straightforward EBU R128 or ATSC A/85 compliance, the preset functions are sufficient. All Set functions return true on success.

Adjust Target

int GetAdjustTargetType(LevelAPI2Ptr)

Returns the current target meter type used for normalization.

bool SetAdjustTargetType(LevelAPI2Ptr, enum MeterTypes)

Sets the meter that Adjust() normalizes onto the target. Accepted values:

  • full mixLU (default), LUFS
  • dialogueLU_Dialog, LUFS_Dialog, LU_Dialog_UNGATED, LUFS_Dialog_UNGATED. Anchor-based practices normalize on dialogue loudness rather than on the programme, which is what SetPresetATSC() and the two anchor-based standard presets select
  • levelPPM, PPM_Max, Peak, TruePeak
Returns false for any other meter type, leaving the setting unchanged.
float GetAdjustTargetLevel(LevelAPI2Ptr)

Returns the target loudness level in dB.

bool SetAdjustTargetLevel(LevelAPI2Ptr, const float targetLevel)

Sets the normalization target level in dB. Default is 0 (0 LU = −23 LUFS for EBU, −24 LKFS for ATSC).

Channels

enum SurroundOrders GetSurroundOrder(LevelAPI2Ptr)

Returns the current surround channel order.

bool SetSurroundOrder(LevelAPI2Ptr, enum SurroundOrders)

Sets the channel order for surround sources. Also automatically configures dialog channel detection. Use SMTPE_ITU_AC3, FilmDolbyDigital, or DTS_ProControl.

bool SetChannelWeight(LevelAPI2Ptr, const int channel, const float weight)

Overrides the per-channel weighting used for loudness measurement. Only relevant for surround configurations. SetSurroundOrder() handles this automatically for standard layouts.

  • channelChannel index, 0 to max_channels
  • weightWeight in dB

Gates

float GetRelativeGate(LevelAPI2Ptr)

Returns the relative gate threshold in dB.

bool SetRelativeGate(LevelAPI2Ptr, const float threshold)

Sets the relative gate threshold. Default: −10 dB (EBU R128), 0 (ATSC, no gating).

bool GetDialogGate(LevelAPI2Ptr)

Returns whether the dialog gate is enabled.

bool SetDialogGate(LevelAPI2Ptr, const bool on)

Enables or disables the dialog gate. Required for ATSC CALM Act compliance on long-form content, and already set by SetPresetATSC() and by the two anchor-based standard presets. Call it directly only to depart from a preset.

Calibration

float GetCalibration(LevelAPI2Ptr, enum MeterTypes)

Returns the calibration offset in dB for a given meter type.

bool SetCalibration(LevelAPI2Ptr, enum MeterTypes, const float calibration)

Sets calibration offset. Defaults: −23 dB for EBU LU, −24 dB for ATSC. Meter type: LU, PPM, or TruePeak.

Limiter

bool GetLimiterEnable(LevelAPI2Ptr)

Returns whether the limiter is active.

bool SetLimiterEnable(LevelAPI2Ptr, const bool on)

Forces the limiter on or off. By default it activates automatically when needed.

float GetLimiterThreshold(LevelAPI2Ptr)

Returns the limiter analysis threshold in dBFS.

bool SetLimiterThreshold(LevelAPI2Ptr, const float dB)

Sets the limiter threshold used during analysis. Default: −1.0 dBFS (prevents TruePeak clipping).

float GetLimiterThresholdAdjust(LevelAPI2Ptr)

Returns the limiter threshold used during the adjustment pass.

bool SetLimiterThresholdAdjust(LevelAPI2Ptr, const float dB)

Sets the limiter threshold for the adjustment pass. May differ from the analysis threshold because TruePeak and sample peak are not identical.

Time Window

int GetTimeWindow(LevelAPI2Ptr)

Returns the current measurement window in seconds.

bool SetTimeWindow(LevelAPI2Ptr, const unsigned int seconds)

Limits the measurement to a rolling time window. Useful for continuous realtime metering. Each additional hour of window increases memory use by ~1.44 MB.

Legacy Meters (PPM / RMS)

PPM and RMS are legacy meters, disabled by default. Enable them only if your integration specifically requires them.

bool SetPPMEnable(LevelAPI2Ptr, const bool)

Enables or disables PPM measurement. Default: off.

bool SetRMSEnable(LevelAPI2Ptr, const bool)

Enables or disables RMS measurement. Default: off.

Histogram Access

Thread safety: Histogram functions are not thread-safe. Complete your read or copy before calling any other function on the same handle.

Raw histogram data is available for detailed loudness distribution analysis:

FunctionReturns
GetHistogramLUFS(handle, &size)Integrated loudness histogram
GetHistogramLUFS_Dialog(handle, &size)Dialog loudness histogram
GetHistogramLUFS_S(handle, &size)Short-term loudness and LRA histogram
GetHistogramPSR(handle, &size)Peak-to-Short-term Ratio histogram

Conversion helpers between histogram positions and dB values:

int LUFS2tablePos(LevelAPI2Ptr, float lufs)

Converts a LUFS value to a histogram array index.

float tablePos2LUFS(LevelAPI2Ptr, int pos)

Converts a histogram array index to a LUFS value.

float dB2float(LevelAPI2Ptr, float dB)

Converts a dB value to a linear float.

float float2dB(LevelAPI2Ptr, float flt)

Converts a linear float to dB.

11 Loudness Standards

A technician handed a delivery spec does not want to configure a meter. They want to pick the name printed on the document their broadcaster gave them. SetPresetStandard() takes one value from LoudnessStandards and applies that practice in full: the target, the relative gate, the dialogue gate, the true-peak ceiling, and which meter normalization moves onto the target.

Ten standards ship in this build. The library can also list and describe them at runtime, so a preset dropdown is generated from the loaded library rather than typed out by hand. A library update then adds standards to your product without a line of code changing.

For the plain-language version, with what each practice asks for and why the variants differ, see levelapi.dev/loudness-standards.

LKFS and LUFS are two names for the same unit. LKFS is the ITU and ATSC spelling, used in ITU-R BS.1770, ATSC A/85, ARIB TR-B32 and Free TV OP-59; LUFS is the EBU spelling, used in EBU R 128. The measurement is identical and the numbers are identical, and 1 LU equals 1 dB either way. Each row below is spelled the way its own document spells it.

What each preset applies

A relative gate of 0 means ungated. The normalizes column is the meter Adjust() moves onto the target: an anchor-based practice normalizes dialogue loudness rather than the full mix. The limiter threshold is set but the limiter is never switched on or off by a preset, so a preset cannot move your audio for a reason you did not ask for.

StandardConstantTargetRelative gateDialogue gateCeilingNormalizes
EBU R 128EBU_R128−23 LUFS−10 LUoff−1 dBTPLU
EBU R 128 (2010 edition)EBU_R128_2010−23 LUFS−8 LUoff−1 dBTPLU
EBU R 128 s2, streamingEBU_R128_S2_STREAMING−18 LUFS−10 LUoff−1 dBTPLU
EBU R 128 s4, cinematicEBU_R128_S4_CINEMA−23 LUFS−10 LUon−1 dBTPLU_Dialog
ATSC A/85, long formATSC_A85_LONGFORM−24 LKFS0, ungatedon−2 dBTPLU_Dialog
ATSC A/85, short formATSC_A85_SHORTFORM−24 LKFS−10 LUoff−2 dBTPLU
ARIB TR-B32ARIB_TRB32−24 LKFS−10 LUoff−1 dBTPLU
Free TV Australia OP-59FREETV_OP59−24 LKFS−10 LUoff−2 dBTPLU
ITU-R BS.1770-1 (ungated)ITU_BS1770_1−23 LUFS0, ungatedoff−1 dBTPLU
ITU-R BS.1770-4 (gated)ITU_BS1770_4−23 LUFS−10 LUoff−1 dBTPLU

Constants are shown without their LEVELAPI2_STANDARD_ prefix. The full names and their numeric values are in section 05.

ATSC A/85's two variants use opposite settings, and the difference is content length. Long-form programme material is measured dialogue-gated with relative gating off. Short-form material, meaning commercials and promos, is measured on the full mix over its whole duration with relative gating on. Picking the wrong one of the two is the most common way to be out of compliance while believing you are in it.

Two rows are anchor-based and behave differently from the rest: EBU_R128_S4_CINEMA and ATSC_A85_LONGFORM normalize dialogue loudness rather than programme loudness. The dialogue meters fall back to the full mix on their own when the material carries less than 10% dialogue.

ITU-R BS.1770 publishes no target level. It defines a measurement, not a delivery practice, so the two ITU rows carry the library's own −23 default in order to be usable on their own. BS.1770 spells the unit LKFS; those two rows are labelled LUFS here because that is what the library reports for them at runtime through GetStandardDescription().

Function reference

bool SetPresetStandard(LevelAPI2Ptr handle, enum LoudnessStandards)

Applies one standard in full. Sets the calibration, the adjust target type and level, the relative gate, the dialogue gate and the limiter threshold. It touches nothing else: channel weights, surround order and the time window are left exactly as you set them.

Returns false if this build does not know the value, and in that case changes nothing. Always check it.
enum LoudnessStandards GetPresetStandard(LevelAPI2Ptr handle)

Returns the standard last applied, or LEVELAPI2_STANDARD_NONE if none has been. Note that this is the last value set: changing a constituent setting by hand afterwards, with SetRelativeGate() for instance, does not clear it. Treat it as a record of what was selected, not as proof of the current configuration. To report what is actually in force, read the individual getters.

int GetStandardCount(LevelAPI2Ptr handle)

Returns how many standards this build knows.

enum LoudnessStandards GetStandardAt(LevelAPI2Ptr handle, const int index)

Returns the standard at a position, for walking the list. Use with GetStandardCount() to build a menu that cannot disagree with the loaded library.

  • index0 to GetStandardCount() − 1. Out of range returns LEVELAPI2_STANDARD_NONE
const char* GetStandardName(LevelAPI2Ptr handle, enum LoudnessStandards)

The display name, for example "ATSC A/85 - long form". Returns an empty string for a value this build does not know. The pointer is owned by the library and stays valid for its lifetime.

const char* GetStandardDescription(LevelAPI2Ptr handle, enum LoudnessStandards)

A one-line description of what the preset actually applies, for example "−24 LKFS, dialogue gate on, NO relative gate, −2 dBTP. US broadcast, programmes." Worth surfacing in your interface next to the name: A/85 itself warns that preset labels differ between products and that users should verify the algorithm rather than trust the label.

Building a preset menu from the library

Enumerate rather than hardcode. A host built against a newer header but running against an older library then sees a shorter list, never a wrong answer.

C
// Populate a dropdown with whatever this build supports
int count = api.GetStandardCount(h);
for (int i = 0; i < count; i++)
{
    enum LoudnessStandards s = api.GetStandardAt(h, i);
    add_menu_item(api.GetStandardName(h, s),
                  api.GetStandardDescription(h, s),
                  (int)s);
}

// Apply the user's choice, and check the result
if (!api.SetPresetStandard(h, chosen))
{
    // This library build does not know that standard.
    // Nothing was changed. Do not measure and report anyway.
    report_unsupported_standard(chosen);
}

Check the return value of SetPresetStandard(). An older library handed a newer standard's value refuses it and changes nothing, which is only useful if the caller notices. Unchecked, a version mismatch becomes a measurement made to the wrong practice at the customer's end.

The two original presets

SetPresetEBU_R128() and SetPresetATSC() are unchanged and are not deprecated. They are exactly SetPresetStandard(LEVELAPI2_STANDARD_EBU_R128) and SetPresetStandard(LEVELAPI2_STANDARD_ATSC_A85_LONGFORM). Existing integrations need no edit.

How far these are verified

All ten presets are verified to configure exactly what their own standard's document specifies. Three are additionally verified against that body's own published reference audio: EBU R 128, ITU-R BS.1770 and ARIB TR-B32. The other seven have no published reference set to measure against: only some standards bodies release conformance audio. Full per-file results, with the tolerance and the document clause beside every measurement, are at levelapi.dev/compliance.

These are measurements made in-house against reference files the standards bodies publish. Nothing here has been assessed or certified by the EBU, the ITU, ATSC, ARIB or Free TV Australia.

12 Technical Specifications

EBU R128

MeasurementITU BS.1770-2
Relative gate−10 LU
0 LU equals−23 LUFS
Target tolerance0 LU ± 6 LU

ATSC A/85

MeasurementITU BS.1770-1
Relative gateNone
0 LU equals−24 LKFS
Target tolerance0 LU ± 6 LU

Surround channel orders

ModeChannel order
SMPTE / ITUL, R, C, Lfe, Ls, Rs
Film / DolbyL, C, R, Ls, Rs, Lfe
DTSL, R, Ls, Rs, C, Lfe

13 Realtime Adjuster

The realtime adjuster holds a live signal on a loudness target while the audio is passing through it. It measures and corrects in a single pass, with no analysis stage and no knowledge of what is coming next, which is what allows it to sit in a path where the audio has to come out now: a live console insert, a monitor path, a stream encoder, a broadcast chain.

Most of the time it only rides the gain, slowly and in small steps, so the dynamics of the material are left alone. A sudden jump in level is too fast for that ride, so a peak stage catches it within a fraction of a second: a short cut, a gentle compressor that is active only while the cut is, and a true-peak limiter. The ride then takes the correction over and the peak stage lets go.

EBU R128 asks produced programme to sit within ±0.5 LU of target, and permits ±1.0 LU on less predictable material such as live mixed programme. That wider window is the one the adjuster works to, once it is warmed up. Treat it as a target rather than a guarantee: difficult live material with extreme dynamics can land outside the band.

The adjuster is a separate object from the measurement API, with its own handle type (LevelAPI2_RealtimeAdjusterPtr) and its own create and destroy calls. A single process can run both, and any number of either.

Realtime adjuster

Target band±1.0 LU (R128 live)
Latency50 ms, fixed
Peak ceiling-1 dBTP (true peak)
CorrectionGain ride + peak stage

Stream limits

Maximum channels16
Frames per callAny
Sample format32-bit float
BuffersAdjusted in place

Dialogue gating is a library capability, not an adjuster one. LevelAPI2 supports ATSC A/85 with dialogue intelligence. The realtime adjuster does not currently perform dialogue gating, so for A/85 workflows that depend on it, use the measurement API. The library is under active development, and extensions or custom integrations can be arranged under a license. If your product needs something that is not covered here, get in touch.

Integration overview

  1. Load the library and validate your certificate

    Identical to the measurement API. SetCertificate() is a global call made once per process after loading, not once per handle.

  2. Create an adjuster

    Create_RealtimeAdjuster(channels, samplerate, bitdepth) returns the handle every other call takes as its first argument. There is no standard preset pair to choose here: the adjuster works to a LUFS target you set directly.

  3. Set the preset and the target

    The constructor takes neither, so both are applied immediately afterwards with RealtimeAdjuster_SetPreset() and RealtimeAdjuster_SetTargetLUFS().

  4. Call Adjust() on every block

    The buffer is modified in place. Nothing else has to happen per block, and no result has to be read for the adjuster to do its job.

C
#include "LevelAPI2_dynlib.h"

// 1. Load library and validate certificate (once per process)
struct LevelAPI2_FunctionHandles api;
void* lib = LevelAPI2_LoadDLL(library_path, &api);
api.SetCertificate(cert_string, strlen(cert_string));

// 2. Create an adjuster instance
LevelAPI2_RealtimeAdjusterPtr h =
    api.Create_RealtimeAdjuster(channels, samplerate, bitdepth);

// 3. Configure: preset and target loudness
api.RealtimeAdjuster_SetPreset(h, 1);          // 1 = medium (default)
api.RealtimeAdjuster_SetTargetLUFS(h, -23.0f); // EBU R128 programme target

// 4. Per audio block: adjust in place (planar buffers)
api.RealtimeAdjuster_Adjust(h, channel_pointers, numframes);

// Optional: read what it is doing, for display
float applied = api.RealtimeAdjuster_GetLastAppliedGainDb(h);
float lufs    = api.RealtimeAdjuster_GetLUFS(h);

// With video: delay the picture by the adjuster's latency to keep lip-sync.
// The loader leaves a function NULL on an older library, so check it first.
unsigned int delay = api.RealtimeAdjuster_GetLatencySamples
    ? api.RealtimeAdjuster_GetLatencySamples(h) : 0;

// Cleanup
api.Destroy_RealtimeAdjuster(h);
LevelAPI2_UnloadDLL(lib);

That is the whole integration. Everything below is optional refinement.

Latency and the limiter

The peak stage looks ahead, so the adjuster delays the audio by 50 ms: 2205 frames at 44.1 kHz, 2400 at 48 kHz. The delay is fixed from Create_RealtimeAdjuster() or Reset() until the next Reset(), whatever the preset or the limiter switch, so a host can compensate for it once. RealtimeAdjuster_GetLatencySamples() reports it in frames; delay the picture by that amount to keep lip-sync.

The limiter holds the output at -1 dBTP by default, measured as true peak on a 4x oversampled signal, so the inter-sample peaks that a sample-peak limiter misses are caught as well. Delivery specifications differ (ATSC A/85 and Free TV OP-59 ask for -2 dBTP), so the ceiling is a setting: RealtimeAdjuster_SetTruePeakCeilingDb(handle, -2.0f) takes anything from -12 to 0 dBTP and is kept across Reset(). Lowered mid-stream, the new ceiling is reached within 20 ms.

The limiter holds the ceiling on its own 4x estimate. On material with strong content close to half the sample rate (noise, some synthesised or lossy sources) a BS.1770 true-peak meter can read up to about 0.6 dB higher. For a guarantee on such material, set the ceiling that much lower. On programme material the two agree.

RealtimeAdjuster_SetLimiterEnabled(handle, false) switches the limiter off if your chain already has one. The 50 ms delay stays, and so does the rest of the peak stage. Without the limiter a fast transient can clip, so peak protection has to exist somewhere in your chain.

Warmup and settling

The adjuster corrects from the first block of audio. There is no warmup period to wait through, and no opening stretch during which the output plays closer to the input level than to the target.

Warmup is a property of the preset rather than of the adjuster, so a preset can choose to ease its correction in instead. No preset in this version does. Because the value comes from the preset, calling SetPreset() mid-stream can change it.

Separately, the adjuster's own integrated meter needs audio behind it before the loudness it reports means anything, so calibration strength ramps in over 5 seconds on every preset. That clock starts where the main warmup ends, so under a preset that has one the total wait is the two added together; with no warmup it starts at the first block. The moment it reaches zero is also the moment GetLUFS() is fully settled, which makes it the honest gate for anything that displays or logs a measured level.

Four read-outs report the two clocks, all in seconds:

  • GetWarmupSecondsLeft() and GetCalibrationSecondsLeft() count down to 0 and stay there. <= 0 is the test for done.
  • GetWarmupSecondsMax() and GetCalibrationSecondsMax() report the lengths the current preset is configured for, so a countdown can be drawn without hardcoding a duration. A maximum of 0 means the preset has no such period at all.

A completed warmup is latched. Switching preset while audio is running can shorten a warmup still in progress, but it never re-opens one that has already finished.

Presets

ValuePresetNotes
0SlowBehaves as Medium in this version, until it has its own tuning.
1MediumThe tuned default and the recommended choice.
2FastBehaves as Medium in this version, until it has its own tuning.

SetPreset() takes a plain unsigned int, so the legal values are listed here rather than expressed as an enum across the library boundary. Once tuned, the presets differ in how quickly the adjuster responds and how strongly it weights recent audio against the programme so far. They are close to each other by design: the intent is a stable output level, and the reason to change preset is that the output is not reaching the target, not that you want a different sound.

Medium is what LevelAnchor ships with, hard-coded, and it is what has been tuned against real material. In this version Slow and Fast use Medium's values: the values 0 and 2 are still accepted and returned, so code that selects them keeps working, and each gets its own tuning in a later version.

Threading and lifecycle

Create, Reset and SetProgramLengthSeconds are heavy calls. Never call them from your audio thread, and never while an Adjust call is running. They allocate and initialize the adjuster's internal meters: roughly 50 MB per instance.

Adjust() and AdjustInterleaved() never allocate memory and never take a lock, so they are safe on a realtime audio thread. Every other getter and setter is safe from any thread, with one exception: GetLevel() reads a live meter, so call it from the thread that calls Adjust(), between calls. A setter takes effect at the start of the next Adjust() call, and the loudness ride acts on it at its next 10 ms step.

One adjuster instance handles one stream. Create as many as you have streams. Reset() starts a fresh stream, optionally in a new format: nothing the previous stream measured or decided carries over. The preset, the target, the limiter switch and the program length are kept.

Buffers are modified in place. Adjust() takes planar channel pointers, AdjustInterleaved() takes one interleaved buffer. Maximum 16 channels, and any number of frames per call.

Internally the adjuster decides on a 10 ms grid, counted from the start of the stream. The output does not depend on the host's block size: any number of frames per call gives exactly the same output, sample for sample. With dither on, the dither noise is the same noise drawn in a different order. Very small blocks cost more CPU per second of audio: 64-frame blocks take about twice the CPU of 10 ms blocks.

Setting the target with SetTargetLUFS() while audio is running is supported and is the normal way to change target. The adjuster ramps toward the new value rather than jumping to it.

Continuous feeds (24/7)

By default the adjuster's long-term correction looks at the whole stream since Create_RealtimeAdjuster() or the last Reset(). That suits a programme with a beginning and an end, with a Reset() at its start.

A feed that runs for hours or days should set a program length instead, so the correction keeps responding when the feed's loudness changes. After a day of audio, one new hour is too small a share of the whole to move it. 900 seconds is the tested value.

C
// Right after Create, before any audio: costs nothing there
api.RealtimeAdjuster_SetProgramLengthSeconds(h, 900);

On material up to an hour long, 900 s changes nothing for the first 15 minutes and keeps the programme's integrated level where the default puts it, within 0.06 LU. Later in a long programme it holds the short-term level inside ±1 LU slightly more often, for 1 to 3 % more gain movement. It costs nothing measurable on shorter programmes. Set mid-stream, it restarts the long-term correction from an empty meter; the gain does not jump.

Measured behaviour

The adjuster is measured by a published test method on 113 cases at -23 LUFS and 67 at -16 LUFS: synthetic level jumps and pauses, broadcast recordings, films, music, audiobooks, conversation and a live stream. The results list every graded criterion with every failing case and its figure, and a table of what a listener hears in each situation and what changes it. In short:

  • Whole programmes end within ±1 LU of the target: all of them at -23 LUFS. At -16 LUFS, material quieter than about -28 LUFS stays under it, because +12 dB is the most the adjuster adds.
  • After a jump to louder material the output is too loud for about half a minute, by up to about 7.5 LU, then settles. A host that knows where the programme changes (a playlist change, an ad insertion) can call Reset() there, off the audio thread.
  • On a feed that runs for hours, set a program length.

Function reference

All functions below take a LevelAPI2_RealtimeAdjusterPtr as their first argument. All Set functions return true on success.

LevelAPI2_RealtimeAdjusterPtr Create_RealtimeAdjuster(const unsigned int channels, const unsigned int samplerate, const unsigned int bitdepth)

Creates an adjuster instance. Returns a valid handle, or NULL on failure. Every subsequent function call requires this handle as its first argument.

  • channelsNumber of audio channels, e.g. 2 for stereo. Maximum 16
  • samplerateSample rate of the stream, e.g. 48000
  • bitdepthThe word length the host will quantise this processor's output to, which decides whether dither is applied on the way out. It describes what happens downstream, not the depth of the incoming material. Pass 0 for a float host: 0, and any value below 8, means no dither at all. Otherwise pass the output depth, e.g. 16 or 24

Not the same argument as Create_LevelAPI2(). The two calls take a parameter of the same name and it means different things: on the two-pass API it is the bit depth of the source material, and here it is the output word length. A float signal chain wants 0 here.

void Destroy_RealtimeAdjuster(LevelAPI2_RealtimeAdjusterPtr handle)

Destroys the instance and frees its resources.

bool RealtimeAdjuster_Adjust(LevelAPI2_RealtimeAdjusterPtr handle, float** samples, const unsigned int numframes)

Applies the adjuster to a block of planar float buffers, in place. Call once per audio block.

  • samplesArray of channel pointers, one float buffer per channel
  • numframesNumber of sample frames per channel. Any number, from one upward
Returns true on success.
bool RealtimeAdjuster_AdjustInterleaved(LevelAPI2_RealtimeAdjusterPtr handle, float* samples, const unsigned int numframes)

Interleaved variant. Use when your audio is in a single interleaved buffer rather than separate channel buffers.

bool RealtimeAdjuster_Reset(LevelAPI2_RealtimeAdjusterPtr handle, const unsigned int channels, const unsigned int samplerate, const unsigned int bitdepth)

Starts a fresh stream, in the format given: nothing the previous stream measured or decided carries over. Keeps the preset, the target, the limiter switch and the program length. A heavy call: never run it on the audio thread.

  • channelsNumber of audio channels in the new stream. Maximum 16
  • samplerateSample rate of the new stream
  • bitdepthOutput word length, exactly as in Create_RealtimeAdjuster() above: 0 for a float host and therefore no dither, otherwise the depth the host quantises to
Returns true on success.
int RealtimeAdjuster_GetPreset(LevelAPI2_RealtimeAdjusterPtr)

Returns the current preset as 0, 1 or 2.

bool RealtimeAdjuster_SetPreset(LevelAPI2_RealtimeAdjusterPtr, const unsigned int preset)

Sets the response preset. See the preset table above for legal values. Default: 1 (medium).

float RealtimeAdjuster_GetTargetLUFS(LevelAPI2_RealtimeAdjusterPtr)

Returns the current target loudness in LUFS.

bool RealtimeAdjuster_SetTargetLUFS(LevelAPI2_RealtimeAdjusterPtr, const float lufs)

Sets the target loudness in LUFS, for example -23.0 for EBU R128 or -24.0 for ATSC A/85. Safe to call while audio is running.

float RealtimeAdjuster_GetLastAppliedGainDb(LevelAPI2_RealtimeAdjusterPtr)

Returns the gain in dB currently being applied, including the peak stage's short cut on a sudden jump in level (so it dips briefly on each one), but not the compressor or the limiter. This is the value to show a user who wants to see the adjuster working.

float RealtimeAdjuster_GetWarmupSecondsLeft(LevelAPI2_RealtimeAdjusterPtr)

Seconds of warmup left before gain correction is at full strength. Counts down to 0 and stays there. Returns 0 throughout on a preset with no warmup, which includes the default.

float RealtimeAdjuster_GetWarmupSecondsMax(LevelAPI2_RealtimeAdjusterPtr)

The warmup length the current preset is configured for, in seconds, so a progress display needs no hardcoded duration. 0 means the preset has no warmup.

float RealtimeAdjuster_GetCalibrationSecondsLeft(LevelAPI2_RealtimeAdjusterPtr)

Seconds left before the integrated meter is settled. Holds at its maximum until the main warmup completes, then counts down to 0. GetLUFS() is fully settled when this reaches 0.

float RealtimeAdjuster_GetCalibrationSecondsMax(LevelAPI2_RealtimeAdjusterPtr)

The calibration ramp the current preset is configured for, in seconds. 5 on every preset at present.

bool RealtimeAdjuster_GetLimiterEnabled(LevelAPI2_RealtimeAdjusterPtr)

Returns whether the built-in peak limiter is active.

bool RealtimeAdjuster_SetLimiterEnabled(LevelAPI2_RealtimeAdjusterPtr, const bool enable)

Enables or disables the limiter. Enabled by default. Disabled, peak protection becomes your responsibility; the latency stays the same.

unsigned int RealtimeAdjuster_GetLatencySamples(LevelAPI2_RealtimeAdjusterPtr)

The delay between a sample going in and the same sample coming out, in frames: 2205 at 44.1 kHz, 2400 at 48 kHz. Constant until the next Reset(), whatever the preset or the limiter switch. Delay the picture by it to keep lip-sync.

bool RealtimeAdjuster_SetTruePeakCeilingDb(LevelAPI2_RealtimeAdjusterPtr, const float dbtp)

The true-peak ceiling the limiter holds, in dBTP. -1.0, the EBU R 128 figure, by default. The limiter holds it on a 4x oversampled true-peak estimate; on material with strong content close to half the sample rate a BS.1770 meter can read up to about 0.6 dB higher. See Latency and the limiter.

  • dbtp-12.0 to 0.0. Anything else returns false and changes nothing
Returns true on success. Kept across Reset(). Safe from any thread and mid-stream: a lower ceiling is reached within 20 ms, and until then peaks stay under the ceiling before it.
float RealtimeAdjuster_GetTruePeakCeilingDb(LevelAPI2_RealtimeAdjusterPtr)

Returns the true-peak ceiling in dBTP.

bool RealtimeAdjuster_SetProgramLengthSeconds(LevelAPI2_RealtimeAdjusterPtr, const unsigned int seconds)

How far back the adjuster's long-term correction looks, in seconds of programme. 0, the default, is the whole stream since Create or the last Reset. A continuous feed sets a length; 900 is the tested value. See Continuous feeds.

  • seconds0, or 900 to 86400. Anything else returns false and changes nothing
Returns true on success. Kept across Reset(). Allocates about 0.9 KB per second of length, so call it off the audio thread; right after Create, before any audio, it costs nothing.
unsigned int RealtimeAdjuster_GetProgramLengthSeconds(LevelAPI2_RealtimeAdjusterPtr)

Returns the program length in seconds, 0 for the whole stream.

Informational read-outs

These report what the adjuster is measuring. None of them is required to run it, and an integration that only needs correct loudness can ignore this section entirely.

float RealtimeAdjuster_GetLUFS(LevelAPI2_RealtimeAdjusterPtr)

Integrated loudness of the adjusted output, in LUFS, over the program length (the whole stream by default). It comes from a meter of its own, started once any preset warmup has completed, and is fully settled when GetCalibrationSecondsLeft() reaches 0.

float RealtimeAdjuster_GetGainReductionDb(LevelAPI2_RealtimeAdjusterPtr)

How far the peak stage held the programme down during the last Adjust() call, in dB, 0 or more. For a gain-reduction meter.

float RealtimeAdjuster_GetLevel(LevelAPI2_RealtimeAdjusterPtr handle, enum MeterTypes meterType, const int channel)

Any value from the MeterTypes enum, read from the adjuster's own output meter. Same semantics as GetLevel() on the measurement API, including -1 for the combined result across all channels.

float RealtimeAdjuster_GetBS1771(LevelAPI2_RealtimeAdjusterPtr)

Realtime BS.1771 loudness of the input, before adjustment. Useful for showing what arrived against what left.

float RealtimeAdjuster_GetPSR(LevelAPI2_RealtimeAdjusterPtr)

Peak-to-Short-term Ratio of the output, over the recent short window.

float RealtimeAdjuster_GetLRA(LevelAPI2_RealtimeAdjusterPtr)

Loudness range of the output, over the recent short window.

LevelAnchor, the free OBS plugin built on this API, deliberately displays none of these. It shows a target and an output level, and nothing else. They are here because a product might want them, not because the adjuster needs them.

Worked example

LevelAnchor, the free OBS loudness plugin, is a complete, shipping integration of this exact header: a Windows VST plugin that holds a live audio source on a LUFS target. Its source is public, and the released binary is built from it. The file to read is Source/LevelApiDynamic.cpp, which loads the library, resolves the function table, creates one adjuster per plugin instance and calls Adjust() from the audio callback.


14 LevelShow2: Overview

LevelShow2 is a Windows DirectShow filter that wraps LevelAPI2 for use in Windows media pipeline applications. It is included with every LevelAPI2 license.

It exposes the same measurement and normalization capabilities as LevelAPI2 through two interfaces: a graphical properties page for manual operation, and the ILevelShow2 COM interface for programmatic control.

LevelShow2 is primarily relevant for Windows-based broadcast automation pipelines. For cross-platform or non-DirectShow use, LevelAPI2 directly is the correct integration path.

15 LevelShow2: Usage

LevelShow2 can be implemented in two ways:

  • Single instance: one filter handles both analysis and adjustment in sequence. Simpler workflow, suitable for most use cases.
  • Two instances: one filter analyses, a second applies the adjustment. Allows analysing multiple files first, then normalizing them in a separate pass.

Basic workflow

  1. 1

    Build your DirectShow graph

    Connect your audio source to the LevelShow2 input pin and connect the output pin downstream.

  2. 2

    Configure analysis settings

    Enter your certificate, set Mode to Analyse, select your standard (EBU R128 or ATSC), and if using surround, select the correct channel order.

  3. 3

    Stream the audio

    Run the graph to pass all audio through the filter. LevelShow2 accumulates measurement data throughout the stream.

  4. 4

    Switch to Adjust mode

    Set Mode to Adjust. The filter automatically configures itself with the calculated normalization gain. To reset for a new stream, set Mode to −1.

  5. 5

    Re-stream to apply normalization

    Pass the audio through the filter again. The output is now level-normalized. Alternatively, pass the AdjustLevel value to a second instance set to Adjust mode.

16 LevelShow2: Programmatic Interface

Query the ILevelShow2 interface via IUnknown::QueryInterface to control the filter programmatically. The interface mirrors the graphical controls exactly.

License

MethodDescription
put_Certificate(char*)Supply your license certificate string
get_CertificateError(char*)Retrieve error text if certificate is invalid
get_License(bool*)Returns true if the current certificate is valid

Mode

ValueMeaning
0Analyse mode
1Adjust mode
−1Reset, clears measurement history and returns to Analyse

Analyse settings

MethodValues / DefaultDescription
put/get_MeasureType0=EBU R128, 1=ATSC, 2=CustomConvenience preset, sets all related parameters
put/get_LURelativeGatebool (true=on)Enable/disable the −10 LU relative gate
put/get_DialogGatebool (true=on)Enable/disable dialog gate (ATSC CALM)
put/get_SurroundOrder0=SMPTE, 1=Film, 2=DTSSurround channel order
put/get_CalibrationLUfloat, default −23.00LU calibration offset in dB
put/get_CalibrationPPMfloat, default −9.00PPM calibration offset in dB
put/get_MeterType0=LU, 1=PPM, 2=PeakTarget meter type for normalization
put/get_TargetLevelfloat, default 0.00Target level in dB

Results

MethodDescription
get_LULevel(float*)Integrated loudness in LU
get_PPMLevel(float*)PPM level
get_PeakLevel(float*)True peak level
get_MaxMLevel(float*)Maximum momentary level
get_MaxSLevel(float*)Maximum short-term level
get_LRALevel(float*)Loudness range
get_AdjustLevel(float*)Calculated normalization gain in dB, use with put_AdjustLevel on a second instance
put_AdjustLevel(float)Manually sets the adjustment gain, bypasses analysis
get_MLevel(float*)Current momentary level (call per processed block for realtime metering)
get_SLevel(float*)Current short-term level (call per processed block)
get_Warning(char*)Returns a warning string if the adjust level is abnormal, NULL if none

17 References

  • tech.ebu.ch/loudness: EBU R128 recommendation, official documents, guidelines, and introduction videos
  • ATSC A/85 Recommendation, available from the ATSC website
  • ITU-R BS.1770, the foundational loudness metering standard

A complete trial certificate and the header file (LevelAPI2_dynlib.h) are available on request. Licensing and contact →


18 Release Notes

What changed for users of the library, newest first. The version is the number GetRevision() returns.

202609280

Check these before upgrading

  • The realtime adjuster's output no longer depends on the host's block size: every block size gives what 10 ms blocks give, sample for sample. Code that fed other sizes hears the same level with a slightly different gain path.

New

Fixed

  • After 20 to 45 seconds of digital silence, the returning programme could be up to 7 LU too loud for several seconds.
  • After a realtime Reset() to another sample rate, one internal clock kept the old rate.

The compliance example passes 162 of 173 checks, the same on Windows and Linux: full results. The realtime adjuster's row left it for its own results.

202609270

Check these before upgrading

  • The realtime adjuster's latency is now 50 ms (was 20 ms), and it stays 50 ms with the limiter off. Read it with RealtimeAdjuster_GetLatencySamples().
  • The realtime limiter's ceiling is now -1 dBTP true peak (was -1 dBFS sample peak).
  • The Slow and Fast realtime presets behave as Medium for now, so Slow no longer has its 30-second warmup.
  • RealtimeAdjuster_GetLastAppliedGainDb() now dips briefly on a sudden jump in level.
  • A realtime adjuster uses about twice the CPU of the previous version, and roughly 50 MB of memory.
  • PSR can read higher than before on dynamic material.

New

  • A new realtime processing chain, tuned on real programme material: it catches a sudden jump in level within a fraction of a second. See Realtime Adjuster.
  • RealtimeAdjuster_GetLatencySamples(), RealtimeAdjuster_GetGainReductionDb(), and RealtimeAdjuster_SetProgramLengthSeconds() / GetProgramLengthSeconds() for 24/7 feeds.
  • No limit on frames per call, on every analyse, adjust and realtime call.
  • The realtime audio path never allocates memory or takes a lock, and the realtime getters are safe from any thread except GetLevel().

Fixed

  • Realtime Reset() now starts a completely fresh stream.
  • AnalyseInterleaved() could write past its buffer on a very large block.
  • LRA and PSR read wrong after Init() was called again on the same handle.
  • PSR could dip below zero after a pause.
  • Dither no longer uses the system random generator: it is the same on every platform, takes no lock on Linux, and leaves your own use of rand() alone.

The compliance example passes 162 of 174 checks, the same on Windows and Linux: full results.

202609080

  • Ten named loudness standard presets. See Loudness Standards.
  • The compliance example, which measures the published EBU, ITU and ARIB reference files.