|
|
|
@ -7,74 +7,85 @@ from libc.stdlib cimport malloc, free
|
|
|
|
|
class ChannelType(enum.IntEnum):
|
|
|
|
|
'''Use these values when setting the channel map with
|
|
|
|
|
R128State.set_channel(). See definitions in ITU R-REC-BS 1770-4.
|
|
|
|
|
|
|
|
|
|
Note:
|
|
|
|
|
The ITU-R BS.1770-4 does not include the LFE channel in any of its
|
|
|
|
|
algorithms. For this channel, use the `UNUSED` attribute.
|
|
|
|
|
'''
|
|
|
|
|
Unused = 0 # unused channel (for example LFE channel)
|
|
|
|
|
Left = 1
|
|
|
|
|
Mplus030 = 1 # itu M+030
|
|
|
|
|
Right = 2
|
|
|
|
|
Mminus030 = 2 # itu M-030
|
|
|
|
|
Center = 3
|
|
|
|
|
Mplus000 = 3 # itu M+000
|
|
|
|
|
LeftSurround = 4
|
|
|
|
|
Mplus110 = 4 # itu M+110
|
|
|
|
|
RightSuround = 5
|
|
|
|
|
Mminus110 = 5 # itu M-110
|
|
|
|
|
DualMono = 6 # a channel that is counted twice
|
|
|
|
|
MplusSC = 7 # itu M+SC
|
|
|
|
|
MminusSC = 8 # itu M-SC
|
|
|
|
|
Mplus060 = 9 # itu M+060
|
|
|
|
|
Mminus060 = 10 # itu M-060
|
|
|
|
|
Mplus090 = 11 # itu M+090
|
|
|
|
|
Mminus090 = 12 # itu M-090
|
|
|
|
|
Mplus135 = 13 # itu M+135
|
|
|
|
|
Mminus135 = 14 # itu M-135
|
|
|
|
|
Mplus180 = 15 # itu M+180
|
|
|
|
|
Uplus000 = 16 # itu U+000
|
|
|
|
|
Uplus030 = 17 # itu U+030
|
|
|
|
|
Uminus030 = 18 # itu U-030
|
|
|
|
|
Uplus045 = 19 # itu U+045
|
|
|
|
|
Uminus045 = 20 # itu U-030
|
|
|
|
|
Uplus090 = 21 # itu U+090
|
|
|
|
|
Uminus090 = 22 # itu U-090
|
|
|
|
|
Uplus110 = 23 # itu U+110
|
|
|
|
|
Uminus110 = 24 # itu U-110
|
|
|
|
|
Uplus135 = 25 # itu U+135
|
|
|
|
|
Uminus135 = 26 # itu U-135
|
|
|
|
|
Uplus180 = 27 # itu U+180
|
|
|
|
|
Tplus000 = 28 # itu T+000
|
|
|
|
|
Bplus000 = 29 # itu B+000
|
|
|
|
|
Bplus045 = 30 # itu B+045
|
|
|
|
|
Bminus045 = 31 # itu B-045
|
|
|
|
|
UNUSED = 0
|
|
|
|
|
LEFT = 1
|
|
|
|
|
M_PLUS_030 = 1 # ITU M+030
|
|
|
|
|
RIGHT = 2
|
|
|
|
|
M_MINUS_030 = 2 # ITU M-030
|
|
|
|
|
CENTER = 3
|
|
|
|
|
M_PLUS_000 = 3 # ITU M+000
|
|
|
|
|
LEFT_SURROUND = 4
|
|
|
|
|
M_PLUS_110 = 4 # ITU M+110
|
|
|
|
|
RIGHT_SUROUND = 5
|
|
|
|
|
M_MINUS_110 = 5 # ITU M-110
|
|
|
|
|
DUAL_MONO = 6 # A channel that's counted twice
|
|
|
|
|
M_PLUS_SC = 7 # ITU M+SC
|
|
|
|
|
M_MINUS_SC = 8 # ITU M-SC
|
|
|
|
|
M_PLUS_060 = 9 # ITU M+060
|
|
|
|
|
M_MINUS_060 = 10 # ITU M-060
|
|
|
|
|
M_PLUS_090 = 11 # ITU M+090
|
|
|
|
|
M_MINUS_090 = 12 # ITU M-090
|
|
|
|
|
M_PLUS_135 = 13 # ITU M+135
|
|
|
|
|
M_MINUS_135 = 14 # ITU M+135
|
|
|
|
|
M_PLUS_180 = 15 # ITU M+180
|
|
|
|
|
U_PLUS_000 = 16 # ITU U+000
|
|
|
|
|
U_PLUS_030 = 17 # ITU U+030
|
|
|
|
|
U_MINUS_030 = 18 # ITU U-030
|
|
|
|
|
U_PLUS_045 = 19 # ITU U+045
|
|
|
|
|
U_MINUS_045 = 20 # ITU U-045
|
|
|
|
|
U_PLUS_090 = 21 # ITU U+090
|
|
|
|
|
U_MINUS_090 = 22 # ITU U-090
|
|
|
|
|
U_PLUS_110 = 23 # ITU U+110
|
|
|
|
|
U_MINUS_110 = 24 # ITU U-110
|
|
|
|
|
U_PLUS_135 = 25 # ITU U+135
|
|
|
|
|
U_MINUS_135 = 26 # ITU U-135
|
|
|
|
|
U_PLUS_180 = 27 # ITU U+180
|
|
|
|
|
T_PLUS_000 = 28 # ITU T+000
|
|
|
|
|
B_PLUS_000 = 29 # ITU B+000
|
|
|
|
|
B_PLUS_045 = 30 # ITU B+045
|
|
|
|
|
B_MINUS_045 = 31 # ITU B-045
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ErrorCode(enum.IntEnum):
|
|
|
|
|
'''Error codes returned by libebur128 functions.'''
|
|
|
|
|
Success = 0
|
|
|
|
|
OutOfMemory = 1
|
|
|
|
|
InvalidMode = 2
|
|
|
|
|
InvalidChannelIndex = 3
|
|
|
|
|
ValueDidNotChange = 4
|
|
|
|
|
'''Error codes returned by libebur128's functions.'''
|
|
|
|
|
SUCCESS = 0
|
|
|
|
|
OUT_OF_MEMORY = 1
|
|
|
|
|
INVALID_MODE = 2
|
|
|
|
|
INVALID_CHANNEL_INDEX = 3
|
|
|
|
|
VALUE_DID_NOT_CHANGE = 4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MeasurementMode(enum.IntFlag):
|
|
|
|
|
'''Use these values bitwise OR'd when instancing an R128State class.
|
|
|
|
|
Try to use the lowest possible modes that suit your needs, as performance
|
|
|
|
|
will be better.
|
|
|
|
|
'''Various modes of measurement which can be used. These can be bitwise
|
|
|
|
|
OR'd together to allow a combination of modes.
|
|
|
|
|
'''
|
|
|
|
|
# can call get_loudness_momentary
|
|
|
|
|
ModeM = (1 << 0)
|
|
|
|
|
# can call get_loudness_shortterm
|
|
|
|
|
ModeS = (1 << 1) | ModeM
|
|
|
|
|
# can call get_loudness_global_* and get_relative_threshold
|
|
|
|
|
ModeI = (1 << 2) | ModeM
|
|
|
|
|
# can call get_loudness_range
|
|
|
|
|
ModeLRA = (1 << 3) | ModeS
|
|
|
|
|
# can call get_sample_peak
|
|
|
|
|
ModeSamplePeak = (1 << 4) | ModeM
|
|
|
|
|
# can call get_true_peak
|
|
|
|
|
ModeTruePeak = (1 << 5) | ModeM | ModeSamplePeak
|
|
|
|
|
# uses histogram algorithm to calculate loudness
|
|
|
|
|
ModeHistogram = (1 << 6)
|
|
|
|
|
# Can call get_loudness_momentary.
|
|
|
|
|
MODE_M = (1 << 0)
|
|
|
|
|
|
|
|
|
|
# Can call get_loudness_shortterm and get_loudness_momentary.
|
|
|
|
|
MODE_S = (1 << 1) | MODE_M
|
|
|
|
|
|
|
|
|
|
# Can call get_loudness_global, get_loudness_global_multiple,
|
|
|
|
|
# get_relative_threshold, and get_loudness_momentary.
|
|
|
|
|
MODE_I = (1 << 2) | MODE_M
|
|
|
|
|
|
|
|
|
|
# Can call get_loudness_range, get_loudness_shortterm, and
|
|
|
|
|
# get_loudness_momentary.
|
|
|
|
|
MODE_LRA = (1 << 3) | MODE_S
|
|
|
|
|
|
|
|
|
|
# Can call get_sample_peak and get_loudness_momentary.
|
|
|
|
|
MODE_SAMPLE_PEAK = (1 << 4) | MODE_M
|
|
|
|
|
|
|
|
|
|
# Can call get_true_peak, get_sample_peak, and get_loudness_momentary.
|
|
|
|
|
MODE_TRUE_PEAK = (1 << 5) | MODE_SAMPLE_PEAK | MODE_M
|
|
|
|
|
|
|
|
|
|
# Uses histogram algorithm to calculate loudness.
|
|
|
|
|
MODE_HISTOGRAM = (1 << 6)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ctypedef fused const_frames_array:
|
|
|
|
@ -85,16 +96,8 @@ ctypedef fused const_frames_array:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class R128State:
|
|
|
|
|
'''This is a class representation of an EBU R128 Loudness Measurement State.
|
|
|
|
|
|
|
|
|
|
:param channels: The number of audio channels used in the measurement.
|
|
|
|
|
:type channels: int
|
|
|
|
|
:param samplerate: The samplerate in samples per second (or Hz).
|
|
|
|
|
:type samplerate: int
|
|
|
|
|
:param mode: A bitwise OR'd value from the :class:`Mode` enum. Try to use
|
|
|
|
|
the lowest possible modes that suit your needs, as performance will be
|
|
|
|
|
better.
|
|
|
|
|
:type mode: int
|
|
|
|
|
'''This is a class representation of an EBU R128 Loudness Measurement
|
|
|
|
|
State.
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
# Contains information about the state of a loudness measurement.
|
|
|
|
@ -105,20 +108,36 @@ cdef class R128State:
|
|
|
|
|
unsigned int channels,
|
|
|
|
|
unsigned long samplerate,
|
|
|
|
|
int mode):
|
|
|
|
|
'''Initialize library state.
|
|
|
|
|
|
|
|
|
|
:raises MemoryError: If the underlying C-struct cannot be allocated in
|
|
|
|
|
memory.
|
|
|
|
|
'''
|
|
|
|
|
'''Constructor'''
|
|
|
|
|
self._state = ebur128_init(channels, samplerate, mode)
|
|
|
|
|
if self._state == NULL:
|
|
|
|
|
raise MemoryError('Out of memory.')
|
|
|
|
|
|
|
|
|
|
def __dealloc__(self):
|
|
|
|
|
'''Destroy library state.'''
|
|
|
|
|
'''Deconstructor'''
|
|
|
|
|
if self._state != NULL:
|
|
|
|
|
ebur128_destroy(&self._state)
|
|
|
|
|
|
|
|
|
|
# For autodoc purposes only.
|
|
|
|
|
def __init__(self,
|
|
|
|
|
unsigned int channels,
|
|
|
|
|
unsigned long samplerate,
|
|
|
|
|
int mode):
|
|
|
|
|
'''Initialize library state.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
channels (int): The number of audio channels used in the
|
|
|
|
|
measurement.
|
|
|
|
|
samplerate (int): The samplerate in samples per second (or Hz).
|
|
|
|
|
mode (:class:`MeasurementMode` or int): A value from the
|
|
|
|
|
:class:`MeasurementMode` enum. Try to use the lowest possible
|
|
|
|
|
modes that suit your needs as performance will be better.
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
MemoryError: If the underlying C-struct cannot be allocated in
|
|
|
|
|
memory.
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
'''A nicer way of explaining the object.'''
|
|
|
|
|
obj = '<{0}: channels={1}, samplerate={2}, mode={3} at 0x{4:0{5}X}>'
|
|
|
|
@ -152,9 +171,10 @@ cdef class R128State:
|
|
|
|
|
self.change_parameters(self._state.channels, s)
|
|
|
|
|
|
|
|
|
|
property mode:
|
|
|
|
|
''' A bitwise OR'd value from the :class:`Mode` enum. Try to use
|
|
|
|
|
the lowest possible modes that suit your needs, as performance will be
|
|
|
|
|
better.'''
|
|
|
|
|
''' A value from the :class:`MeasurementMode` enum. Try to use the
|
|
|
|
|
lowest possible modes that suit your needs, as performance will be
|
|
|
|
|
better.
|
|
|
|
|
'''
|
|
|
|
|
def __get__(self):
|
|
|
|
|
'''mode's getter'''
|
|
|
|
|
if self._state is not NULL:
|
|
|
|
@ -170,67 +190,71 @@ cdef class R128State:
|
|
|
|
|
'''Sets an audio channel to a specific channel type as defined in the
|
|
|
|
|
:class:`ChannelType` enum.
|
|
|
|
|
|
|
|
|
|
:param channel_number: The zero-based channel index.
|
|
|
|
|
:type channel_number: int
|
|
|
|
|
:param channel_type: The channel type from :class:`ChannelType`.
|
|
|
|
|
:type channel_type: int
|
|
|
|
|
Args:
|
|
|
|
|
channel_number (int): The zero-based channel index.
|
|
|
|
|
channel_type (:class:`ChannelType` or int): The channel type.
|
|
|
|
|
|
|
|
|
|
:raises IndexError: If specified channel index is out of bounds.
|
|
|
|
|
Raises:
|
|
|
|
|
IndexError: If specified channel index is out of bounds.
|
|
|
|
|
'''
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_set_channel(self._state,
|
|
|
|
|
channel_number,
|
|
|
|
|
channel_type)
|
|
|
|
|
if result == ErrorCode.InvalidChannelIndex:
|
|
|
|
|
if result == ErrorCode.INVALID_CHANNEL_INDEX:
|
|
|
|
|
raise IndexError('Channel index is out of bounds.')
|
|
|
|
|
|
|
|
|
|
def change_parameters(self,
|
|
|
|
|
unsigned int channels,
|
|
|
|
|
unsigned long samplerate):
|
|
|
|
|
'''Changes number of audio channels and/or the samplerate of the
|
|
|
|
|
loudness measurement. Returns an integer error code.
|
|
|
|
|
'''Changes the number of audio channels and/or the samplerate of the
|
|
|
|
|
measurement state.
|
|
|
|
|
|
|
|
|
|
Note that the channel map will be reset when setting a different number
|
|
|
|
|
of channels. The current unfinished block will be lost.
|
|
|
|
|
Note:
|
|
|
|
|
The channel map will be reset when setting a different number of
|
|
|
|
|
channels. The current unfinished block will be lost.
|
|
|
|
|
|
|
|
|
|
:param channels: New number of audio channels.
|
|
|
|
|
:type channels: int
|
|
|
|
|
:param samplerate: The new samplerate in samples per second (or Hz).
|
|
|
|
|
:type samplerate: int
|
|
|
|
|
Args:
|
|
|
|
|
channels (int): New number of audio channels.
|
|
|
|
|
samplerate (int): The new samplerate in samples per second (or Hz).
|
|
|
|
|
|
|
|
|
|
:raises MemoryError: If not enough memory could be allocated for the
|
|
|
|
|
new values.
|
|
|
|
|
:raises ValueError: If both new values are the same as the currently
|
|
|
|
|
stored values.
|
|
|
|
|
Raises:
|
|
|
|
|
MemoryError: If not enough memory could be allocated for the new
|
|
|
|
|
values.
|
|
|
|
|
ValueError: If both new values are the same as the currently stored
|
|
|
|
|
values.
|
|
|
|
|
'''
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_change_parameters(self._state,
|
|
|
|
|
channels,
|
|
|
|
|
samplerate)
|
|
|
|
|
if result == ErrorCode.OutOfMemory:
|
|
|
|
|
if result == ErrorCode.OUT_OF_MEMORY:
|
|
|
|
|
raise MemoryError('Out of memory.')
|
|
|
|
|
elif result == ErrorCode.ValueDidNotChange:
|
|
|
|
|
elif result == ErrorCode.VALUE_DID_NOT_CHANGE:
|
|
|
|
|
raise ValueError('Channel numbers & sample rate have not changed.')
|
|
|
|
|
|
|
|
|
|
def set_max_window(self, unsigned long window):
|
|
|
|
|
'''Set the maximum duration that will be used for
|
|
|
|
|
:func:`~pyebur128.get_loudness_window`.
|
|
|
|
|
:func:`get_loudness_window`.
|
|
|
|
|
|
|
|
|
|
Note that this destroys the current content of the audio buffer.
|
|
|
|
|
Note:
|
|
|
|
|
This will destroy the current content of the audio buffer in the
|
|
|
|
|
measurement state.
|
|
|
|
|
|
|
|
|
|
:param window: The duration of the window in milliseconds (ms).
|
|
|
|
|
:type window: int
|
|
|
|
|
Args:
|
|
|
|
|
window (int): The duration of the window in milliseconds (ms).
|
|
|
|
|
|
|
|
|
|
:raises MemoryError: If not enough memory could be allocated for the
|
|
|
|
|
new value.
|
|
|
|
|
:raises ValueError: If the new window value is the same as the
|
|
|
|
|
currently stored window value.
|
|
|
|
|
Raises:
|
|
|
|
|
MemoryError: If not enough memory could be allocated for the new
|
|
|
|
|
value.
|
|
|
|
|
ValueError: If the new window value is the same as the currently
|
|
|
|
|
stored window value.
|
|
|
|
|
'''
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_set_max_window(self._state, window)
|
|
|
|
|
if result == ErrorCode.OutOfMemory:
|
|
|
|
|
if result == ErrorCode.OUT_OF_MEMORY:
|
|
|
|
|
raise MemoryError('Out of memory.')
|
|
|
|
|
elif result == ErrorCode.ValueDidNotChange:
|
|
|
|
|
elif result == ErrorCode.VALUE_DID_NOT_CHANGE:
|
|
|
|
|
raise ValueError('Maximum window duration has not changed.')
|
|
|
|
|
|
|
|
|
|
def set_max_history(self, unsigned long history):
|
|
|
|
@ -238,39 +262,47 @@ cdef class R128State:
|
|
|
|
|
More history provides more accurate results, but requires more
|
|
|
|
|
resources.
|
|
|
|
|
|
|
|
|
|
Applies to :func:`~pyebur128.get_loudness_range` and
|
|
|
|
|
:func:`~pyebur128.get_loudness_global` when ``ModeHistogram`` is
|
|
|
|
|
not set from :class:`pyebur128.MeasurementMode`.
|
|
|
|
|
Applies to :func:`get_loudness_range` and :func:`get_loudness_global`
|
|
|
|
|
when ``MODE_HISTOGRAM`` is not set from :class:`MeasurementMode`.
|
|
|
|
|
|
|
|
|
|
Default is ULONG_MAX (at least ~50 days).
|
|
|
|
|
Minimum is 3000ms for ``ModeLRA`` and 400ms for ``ModeM``.
|
|
|
|
|
Default is ULONG_MAX (approximately 50 days).
|
|
|
|
|
Minimum is 3000ms for ``MODE_LRA`` and 400ms for ``MODE_M``.
|
|
|
|
|
|
|
|
|
|
:param history: The duration of history in milliseconds (ms).
|
|
|
|
|
:type history: int
|
|
|
|
|
Args:
|
|
|
|
|
history (int): The duration of history in milliseconds (ms).
|
|
|
|
|
|
|
|
|
|
:raises MemoryError: If not enough memory could be allocated for the
|
|
|
|
|
new value.
|
|
|
|
|
:raises ValueError: If the new history value is the same as the
|
|
|
|
|
currently stored history value.
|
|
|
|
|
Raises:
|
|
|
|
|
MemoryError: If not enough memory could be allocated for the new
|
|
|
|
|
value.
|
|
|
|
|
ValueError: If the new history value is the same as the currently
|
|
|
|
|
stored history value.
|
|
|
|
|
'''
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_set_max_history(self._state, history)
|
|
|
|
|
if result == ErrorCode.ValueDidNotChange:
|
|
|
|
|
if result == ErrorCode.VALUE_DID_NOT_CHANGE:
|
|
|
|
|
raise ValueError('Maximum history duration has not changed.')
|
|
|
|
|
|
|
|
|
|
@cython.boundscheck(False)
|
|
|
|
|
@cython.wraparound(False)
|
|
|
|
|
def add_frames(self, const_frames_array source, size_t frames):
|
|
|
|
|
'''Add frames to be processed.
|
|
|
|
|
'''Add audio frames to be processed.
|
|
|
|
|
|
|
|
|
|
:param source: An array of source frames. Channels must be interleaved.
|
|
|
|
|
:type source: New buffer protocol (PEP-3118) array of short, int, float,
|
|
|
|
|
or double.
|
|
|
|
|
:param frames: The number of frames. (Not the number of samples!)
|
|
|
|
|
:type frames: int
|
|
|
|
|
Note:
|
|
|
|
|
The ``source`` argument can be any one-dimensional array that is
|
|
|
|
|
compliant with Python's new buffer protocol (PEP-3118). It must
|
|
|
|
|
contain values of all `short`, `int`, `float`, or `double`.
|
|
|
|
|
|
|
|
|
|
:raises MemoryError: If not enough memory could be allocated for the
|
|
|
|
|
new frames.
|
|
|
|
|
Args:
|
|
|
|
|
source (see Note): An array of source frames. Channels must be
|
|
|
|
|
interleaved.
|
|
|
|
|
frames (int): The number of frames. (NOT the number of samples!)
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
TypeError: If the source array: 1) is not one dimensional, 2) does
|
|
|
|
|
not use Python's new buffer protocol, or 3) does not contain
|
|
|
|
|
all short, int, float, or double values.
|
|
|
|
|
MemoryError: If not enough memory could be allocated for the new
|
|
|
|
|
frames.
|
|
|
|
|
'''
|
|
|
|
|
cdef int result
|
|
|
|
|
|
|
|
|
@ -290,8 +322,15 @@ cdef class R128State:
|
|
|
|
|
result = ebur128_add_frames_double(self._state,
|
|
|
|
|
&source[0],
|
|
|
|
|
frames)
|
|
|
|
|
else:
|
|
|
|
|
msg = (
|
|
|
|
|
'Source array must be one-dimensional, use the new buffer '
|
|
|
|
|
'protocol, and value type must be all short, int, float, or '
|
|
|
|
|
'double.'
|
|
|
|
|
)
|
|
|
|
|
raise TypeError(msg)
|
|
|
|
|
|
|
|
|
|
if result == ErrorCode.OutOfMemory:
|
|
|
|
|
if result == ErrorCode.OUT_OF_MEMORY:
|
|
|
|
|
raise MemoryError('Out of memory.')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -300,19 +339,22 @@ cdef class R128State:
|
|
|
|
|
cpdef double get_loudness_global(R128State state):
|
|
|
|
|
'''Get the global integrated loudness in LUFS.
|
|
|
|
|
|
|
|
|
|
:param state: An instance of the :class:`R128State` class.
|
|
|
|
|
:type state: R128State
|
|
|
|
|
Args:
|
|
|
|
|
state (:class:`R128State`): An instance of the :class:`R128State`
|
|
|
|
|
class.
|
|
|
|
|
|
|
|
|
|
:raises ValueError: If Mode ``ModeI`` has not been set.
|
|
|
|
|
Raises:
|
|
|
|
|
ValueError: If ``MODE_I`` has not been set from
|
|
|
|
|
:class:`MeasurementMode`.
|
|
|
|
|
|
|
|
|
|
:return: The integrated loudness in LUFS.
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The integrated loudness in LUFS.
|
|
|
|
|
'''
|
|
|
|
|
cdef double lufs
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_loudness_global(state._state, &lufs)
|
|
|
|
|
if result == ErrorCode.InvalidMode:
|
|
|
|
|
raise ValueError('Mode "ModeI" has not been set.')
|
|
|
|
|
if result == ErrorCode.INVALID_MODE:
|
|
|
|
|
raise ValueError('MODE_I has not been set.')
|
|
|
|
|
return lufs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -321,15 +363,18 @@ cpdef double get_loudness_global(R128State state):
|
|
|
|
|
cpdef double get_loudness_global_multiple(list states):
|
|
|
|
|
'''Get the global integrated loudness in LUFS across multiple instances.
|
|
|
|
|
|
|
|
|
|
:param state: A list of :class:`R128State` instances.
|
|
|
|
|
:type state: list of R128State
|
|
|
|
|
Args:
|
|
|
|
|
states (list of :class:`R128State`): A list of :class:`R128State`
|
|
|
|
|
instances.
|
|
|
|
|
|
|
|
|
|
:raises MemoryError: If not enough memory could be allocated for the
|
|
|
|
|
conversion of a Python list to a C array.
|
|
|
|
|
:raises ValueError: If Mode ``ModeI`` has not been set.
|
|
|
|
|
Raises:
|
|
|
|
|
MemoryError: If not enough memory could be allocated for the conversion
|
|
|
|
|
of a Python list to a C array.
|
|
|
|
|
ValueError: If ``MODE_I`` has not been set from
|
|
|
|
|
:class:`MeasurementMode`.
|
|
|
|
|
|
|
|
|
|
:return: The integrated loudness in LUFS.
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The integrated loudness of all states in LUFS.
|
|
|
|
|
'''
|
|
|
|
|
cdef double lufs
|
|
|
|
|
cdef int result
|
|
|
|
@ -339,15 +384,15 @@ cpdef double get_loudness_global_multiple(list states):
|
|
|
|
|
num = len(states)
|
|
|
|
|
state_ptrs = <ebur128_state**>malloc(sizeof(ebur128_state*) * num)
|
|
|
|
|
if state_ptrs == NULL:
|
|
|
|
|
raise MemoryError('Unable to allocate array of R128 states.')
|
|
|
|
|
raise MemoryError('Unable to allocate array of states.')
|
|
|
|
|
|
|
|
|
|
for i in range(num):
|
|
|
|
|
state_ptrs[i] = (<R128State?>states[i])._state
|
|
|
|
|
|
|
|
|
|
result = ebur128_loudness_global_multiple(state_ptrs, num, &lufs)
|
|
|
|
|
free(state_ptrs)
|
|
|
|
|
if result == ErrorCode.InvalidMode:
|
|
|
|
|
raise ValueError('Mode "ModeI" has not been set.')
|
|
|
|
|
if result == ErrorCode.INVALID_MODE:
|
|
|
|
|
raise ValueError('MODE_I has not been set.')
|
|
|
|
|
return lufs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -356,11 +401,12 @@ cpdef double get_loudness_global_multiple(list states):
|
|
|
|
|
cpdef double get_loudness_momentary(R128State state):
|
|
|
|
|
'''Get the momentary loudness (last 400ms) in LUFS.
|
|
|
|
|
|
|
|
|
|
:param state: An instance of the :class:`R128State` class.
|
|
|
|
|
:type state: R128State
|
|
|
|
|
Args:
|
|
|
|
|
state (:class:`R128State`): An instance of the :class:`R128State`
|
|
|
|
|
class.
|
|
|
|
|
|
|
|
|
|
:return: The momentary loudness in LUFS.
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The momentary loudness in LUFS.
|
|
|
|
|
'''
|
|
|
|
|
cdef double lufs
|
|
|
|
|
cdef int result
|
|
|
|
@ -373,44 +419,48 @@ cpdef double get_loudness_momentary(R128State state):
|
|
|
|
|
cpdef double get_loudness_shortterm(R128State state):
|
|
|
|
|
'''Get the short-term loudness (last 3s) in LUFS.
|
|
|
|
|
|
|
|
|
|
:param state: An instance of the :class:`R128State` class.
|
|
|
|
|
:type state: R128State
|
|
|
|
|
Args:
|
|
|
|
|
state (:class:`R128State`): An instance of the :class:`R128State`
|
|
|
|
|
class.
|
|
|
|
|
|
|
|
|
|
:raises ValueError: If Mode ``ModeS`` has not been set.
|
|
|
|
|
Raises:
|
|
|
|
|
ValueError: If ``MODE_S`` has not been set from
|
|
|
|
|
:class:`MeasurementMode`.
|
|
|
|
|
|
|
|
|
|
:return: The short-term loudness in LUFS.
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The short-term loudness in LUFS.
|
|
|
|
|
'''
|
|
|
|
|
cdef double lufs
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_loudness_shortterm(state._state, &lufs)
|
|
|
|
|
if result == ErrorCode.InvalidMode:
|
|
|
|
|
raise ValueError('Mode "ModeS" has not been set.')
|
|
|
|
|
if result == ErrorCode.INVALID_MODE:
|
|
|
|
|
raise ValueError('MODE_S has not been set.')
|
|
|
|
|
return lufs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@cython.boundscheck(False)
|
|
|
|
|
@cython.wraparound(False)
|
|
|
|
|
cpdef double get_loudness_window(R128State state, unsigned long window):
|
|
|
|
|
'''Get loudness of the specified window in LUFS.
|
|
|
|
|
'''Get the loudness of the specified window in LUFS.
|
|
|
|
|
|
|
|
|
|
:param state: An instance of the :class:`R128State` class.
|
|
|
|
|
:type state: R128State
|
|
|
|
|
:param window: The window size in milliseconds (ms) to calculate loudness.
|
|
|
|
|
:type window: int
|
|
|
|
|
Args:
|
|
|
|
|
state (:class:`R128State`): An instance of the :class:`R128State`
|
|
|
|
|
class.
|
|
|
|
|
window (int): The window size in milliseconds (ms).
|
|
|
|
|
|
|
|
|
|
:raises ValueError: If the new window size is larger than the current
|
|
|
|
|
window size stored in state.
|
|
|
|
|
Raises:
|
|
|
|
|
ValueError: If the new window size is larger than the current window
|
|
|
|
|
size stored in state.
|
|
|
|
|
|
|
|
|
|
:return: The loudness in LUFS.
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The loudness in LUFS.
|
|
|
|
|
'''
|
|
|
|
|
cdef double lufs
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_loudness_window(state._state, window, &lufs)
|
|
|
|
|
if result == ErrorCode.InvalidMode:
|
|
|
|
|
if result == ErrorCode.INVALID_MODE:
|
|
|
|
|
msg = (
|
|
|
|
|
'Requested window larger than the current '
|
|
|
|
|
'Requested window is larger than the current '
|
|
|
|
|
'window in the provided state.'
|
|
|
|
|
)
|
|
|
|
|
raise ValueError(msg)
|
|
|
|
@ -422,25 +472,27 @@ cpdef double get_loudness_window(R128State state, unsigned long window):
|
|
|
|
|
cpdef double get_loudness_range(R128State state):
|
|
|
|
|
'''Get loudness range (LRA) of audio in LU.
|
|
|
|
|
|
|
|
|
|
Calculates loudness range according to EBU 3342.
|
|
|
|
|
Calculates the loudness range according to EBU 3342.
|
|
|
|
|
|
|
|
|
|
:param state: An instance of the :class:`R128State` class.
|
|
|
|
|
:type state: R128State
|
|
|
|
|
Args:
|
|
|
|
|
state (:class:`R128State`): An instance of the :class:`R128State`
|
|
|
|
|
class.
|
|
|
|
|
|
|
|
|
|
:raises MemoryError: If not enough memory could be allocated for the
|
|
|
|
|
Raises:
|
|
|
|
|
MemoryError: If not enough memory could be allocated for the
|
|
|
|
|
measurement.
|
|
|
|
|
:raises ValueError: If Mode ``ModeLRA`` has not been set.
|
|
|
|
|
ValueError: If ``MODE_LRA`` has not been set.
|
|
|
|
|
|
|
|
|
|
:return: The loudness range (LRA) in LU.
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The loudness range (LRA) in LU.
|
|
|
|
|
'''
|
|
|
|
|
cdef double lufs
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_loudness_range(state._state, &lufs)
|
|
|
|
|
if result == ErrorCode.OutOfMemory:
|
|
|
|
|
if result == ErrorCode.OUT_OF_MEMORY:
|
|
|
|
|
raise MemoryError('Memory allocation error.')
|
|
|
|
|
elif result == ErrorCode.InvalidMode:
|
|
|
|
|
raise ValueError('Mode "ModeLRA" has not been set.')
|
|
|
|
|
elif result == ErrorCode.INVALID_MODE:
|
|
|
|
|
raise ValueError('MODE_LRA has not been set.')
|
|
|
|
|
return lufs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -451,16 +503,19 @@ cpdef double get_loudness_range_multiple(list states):
|
|
|
|
|
|
|
|
|
|
Calculates loudness range according to EBU 3342.
|
|
|
|
|
|
|
|
|
|
:param state: A list of :class:`R128State` instances.
|
|
|
|
|
:type state: list of R128State
|
|
|
|
|
Args:
|
|
|
|
|
states (list of :class:`R128State`): A list of :class:`R128State`
|
|
|
|
|
instances.
|
|
|
|
|
|
|
|
|
|
:raises MemoryError: If not enough memory could be allocated for the
|
|
|
|
|
Raises:
|
|
|
|
|
MemoryError: If not enough memory could be allocated for the
|
|
|
|
|
measurement or there was a problem with the Python list to C array
|
|
|
|
|
conversion.
|
|
|
|
|
:raises ValueError: If Mode ``ModeLRA`` has not been set.
|
|
|
|
|
ValueError: If ``MODE_LRA`` has not been set from
|
|
|
|
|
:class:`MeasurementMode`.
|
|
|
|
|
|
|
|
|
|
:return: The loudness range (LRA) in LU.
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The loudness range (LRA) in LU.
|
|
|
|
|
'''
|
|
|
|
|
cdef double lufs
|
|
|
|
|
cdef int result
|
|
|
|
@ -477,10 +532,10 @@ cpdef double get_loudness_range_multiple(list states):
|
|
|
|
|
|
|
|
|
|
result = ebur128_loudness_range_multiple(state_ptrs, num, &lufs)
|
|
|
|
|
free(state_ptrs)
|
|
|
|
|
if result == ErrorCode.OutOfMemory:
|
|
|
|
|
if result == ErrorCode.OUT_OF_MEMORY:
|
|
|
|
|
raise MemoryError('Memory allocation error.')
|
|
|
|
|
elif result == ErrorCode.InvalidMode:
|
|
|
|
|
raise ValueError('Mode "ModeLRA" has not been set.')
|
|
|
|
|
elif result == ErrorCode.INVALID_MODE:
|
|
|
|
|
raise ValueError('MODE_LRA has not been set.')
|
|
|
|
|
return lufs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -491,23 +546,24 @@ cpdef double get_sample_peak(R128State state, unsigned int channel_number):
|
|
|
|
|
|
|
|
|
|
The equation to convert to dBFS is: 20 * log10(result).
|
|
|
|
|
|
|
|
|
|
:param state: An instance of the :class:`R128State` class.
|
|
|
|
|
:type state: R128State
|
|
|
|
|
:param channel_number: The index of the channel to analyze.
|
|
|
|
|
:type channel_number: int
|
|
|
|
|
Args:
|
|
|
|
|
state (:class:`R128State`): An instance of the :class:`R128State`
|
|
|
|
|
class.
|
|
|
|
|
channel_number (int): The index of the channel to analyze.
|
|
|
|
|
|
|
|
|
|
:raises ValueError: If Mode ``ModeSamplePeak`` has not been set or the
|
|
|
|
|
channel index is out of bounds.
|
|
|
|
|
Raise:
|
|
|
|
|
ValueError: If ``MODE_SAMPLE_PEAK`` has not been set or the channel
|
|
|
|
|
index is out of bounds.
|
|
|
|
|
|
|
|
|
|
:return: The maximum sample peak (1.0 is 0 dBFS).
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The maximum sample peak (1.0 is 0 dBFS).
|
|
|
|
|
'''
|
|
|
|
|
cdef double max_peak
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_sample_peak(state._state, channel_number, &max_peak)
|
|
|
|
|
if result == ErrorCode.InvalidMode:
|
|
|
|
|
raise ValueError('Mode "ModeSamplePeak" has not been set.')
|
|
|
|
|
elif result == ErrorCode.InvalidChannelIndex:
|
|
|
|
|
if result == ErrorCode.INVALID_MODE:
|
|
|
|
|
raise ValueError('MODE_SAMPLE_PEAK has not been set.')
|
|
|
|
|
elif result == ErrorCode.INVALID_CHANNEL_INDEX:
|
|
|
|
|
raise ValueError('Invalid channel index provided.')
|
|
|
|
|
return max_peak
|
|
|
|
|
|
|
|
|
@ -516,27 +572,29 @@ cpdef double get_sample_peak(R128State state, unsigned int channel_number):
|
|
|
|
|
@cython.wraparound(False)
|
|
|
|
|
cpdef double get_previous_sample_peak(R128State state,
|
|
|
|
|
unsigned int channel_number):
|
|
|
|
|
'''Get maximum sample peak from the last call to add_frames().
|
|
|
|
|
'''Get maximum sample peak from the last call to
|
|
|
|
|
:func:`R128State.add_frames`.
|
|
|
|
|
|
|
|
|
|
The equation to convert to dBFS is: 20 * log10(result).
|
|
|
|
|
|
|
|
|
|
:param state: An instance of the :class:`R128State` class.
|
|
|
|
|
:type state: R128State
|
|
|
|
|
:param channel_number: The index of the channel to analyze.
|
|
|
|
|
:type channel_number: int
|
|
|
|
|
Args:
|
|
|
|
|
state (:class:`R128State`): An instance of the :class:`R128State`
|
|
|
|
|
class.
|
|
|
|
|
channel_number (int): The index of the channel to analyze.
|
|
|
|
|
|
|
|
|
|
:raises ValueError: If Mode ``ModeSamplePeak`` has not been set or the
|
|
|
|
|
channel index is out of bounds.
|
|
|
|
|
Raise:
|
|
|
|
|
ValueError: If ``MODE_SAMPLE_PEAK`` has not been set or the channel
|
|
|
|
|
index is out of bounds.
|
|
|
|
|
|
|
|
|
|
:return: The maximum sample peak (1.0 is 0 dBFS).
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The maximum sample peak (1.0 is 0 dBFS).
|
|
|
|
|
'''
|
|
|
|
|
cdef double max_peak
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_prev_sample_peak(state._state, channel_number, &max_peak)
|
|
|
|
|
if result == ErrorCode.InvalidMode:
|
|
|
|
|
raise ValueError('Mode "ModeSamplePeak" has not been set.')
|
|
|
|
|
elif result == ErrorCode.InvalidChannelIndex:
|
|
|
|
|
if result == ErrorCode.INVALID_MODE:
|
|
|
|
|
raise ValueError('MODE_SAMPLE_PEAK has not been set.')
|
|
|
|
|
elif result == ErrorCode.INVALID_CHANNEL_INDEX:
|
|
|
|
|
raise ValueError('Invalid channel index provided.')
|
|
|
|
|
return max_peak
|
|
|
|
|
|
|
|
|
@ -556,23 +614,24 @@ cpdef double get_true_peak(R128State state, unsigned int channel_number):
|
|
|
|
|
|
|
|
|
|
The equation to convert to dBTP is: 20 * log10(out)
|
|
|
|
|
|
|
|
|
|
:param state: An instance of the :class:`R128State` class.
|
|
|
|
|
:type state: R128State
|
|
|
|
|
:param channel_number: The index of the channel to analyze.
|
|
|
|
|
:type channel_number: int
|
|
|
|
|
Args:
|
|
|
|
|
state (:class:`R128State`): An instance of the :class:`R128State`
|
|
|
|
|
class.
|
|
|
|
|
channel_number (int): The index of the channel to analyze.
|
|
|
|
|
|
|
|
|
|
:raises ValueError: If Mode ``ModeTruePeak`` has not been set or the
|
|
|
|
|
channel index is out of bounds.
|
|
|
|
|
Raise:
|
|
|
|
|
ValueError: If ``MODE_TRUE_PEAK`` has not been set or the channel index
|
|
|
|
|
is out of bounds.
|
|
|
|
|
|
|
|
|
|
:return: The maximum true peak (1.0 is 0 dBTP).
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The maximum true peak (1.0 is 0 dBTP).
|
|
|
|
|
'''
|
|
|
|
|
cdef double max_peak
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_true_peak(state._state, channel_number, &max_peak)
|
|
|
|
|
if result == ErrorCode.InvalidMode:
|
|
|
|
|
raise ValueError('Mode "ModeTruePeak" has not been set.')
|
|
|
|
|
elif result == ErrorCode.InvalidChannelIndex:
|
|
|
|
|
if result == ErrorCode.INVALID_MODE:
|
|
|
|
|
raise ValueError('MODE_TRUE_PEAK has not been set.')
|
|
|
|
|
elif result == ErrorCode.INVALID_CHANNEL_INDEX:
|
|
|
|
|
raise ValueError('Invalid channel index provided.')
|
|
|
|
|
return max_peak
|
|
|
|
|
|
|
|
|
@ -581,7 +640,8 @@ cpdef double get_true_peak(R128State state, unsigned int channel_number):
|
|
|
|
|
@cython.wraparound(False)
|
|
|
|
|
cpdef double get_previous_true_peak(R128State state,
|
|
|
|
|
unsigned int channel_number):
|
|
|
|
|
'''Get maximum true peak from the last call to add_frames().
|
|
|
|
|
'''Get maximum true peak from the last call to
|
|
|
|
|
:func:`R128State.add_frames`.
|
|
|
|
|
|
|
|
|
|
Uses an implementation defined algorithm to calculate the true peak. Do not
|
|
|
|
|
try to compare resulting values across different versions of the library,
|
|
|
|
@ -593,23 +653,24 @@ cpdef double get_previous_true_peak(R128State state,
|
|
|
|
|
|
|
|
|
|
The equation to convert to dBTP is: 20 * log10(out)
|
|
|
|
|
|
|
|
|
|
:param state: An instance of the :class:`R128State` class.
|
|
|
|
|
:type state: R128State
|
|
|
|
|
:param channel_number: The index of the channel to analyze.
|
|
|
|
|
:type channel_number: int
|
|
|
|
|
Args:
|
|
|
|
|
state (:class:`R128State`): An instance of the :class:`R128State`
|
|
|
|
|
class.
|
|
|
|
|
channel_number (int): The index of the channel to analyze.
|
|
|
|
|
|
|
|
|
|
:raises ValueError: If Mode ``ModeTruePeak`` has not been set or the
|
|
|
|
|
channel index is out of bounds.
|
|
|
|
|
Raise:
|
|
|
|
|
ValueError: If ``MODE_TRUE_PEAK`` has not been set or the channel index
|
|
|
|
|
is out of bounds.
|
|
|
|
|
|
|
|
|
|
:return: The maximum true peak (1.0 is 0 dBTP).
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The maximum true peak (1.0 is 0 dBTP).
|
|
|
|
|
'''
|
|
|
|
|
cdef double max_peak
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_prev_true_peak(state._state, channel_number, &max_peak)
|
|
|
|
|
if result == ErrorCode.InvalidMode:
|
|
|
|
|
raise ValueError('Mode "ModeTruePeak" has not been set.')
|
|
|
|
|
elif result == ErrorCode.InvalidChannelIndex:
|
|
|
|
|
if result == ErrorCode.INVALID_MODE:
|
|
|
|
|
raise ValueError('MODE_TRUE_PEAK has not been set.')
|
|
|
|
|
elif result == ErrorCode.INVALID_CHANNEL_INDEX:
|
|
|
|
|
raise ValueError('Invalid channel index provided.')
|
|
|
|
|
return max_peak
|
|
|
|
|
|
|
|
|
@ -619,29 +680,31 @@ cpdef double get_previous_true_peak(R128State state,
|
|
|
|
|
cpdef double get_relative_threshold(R128State state):
|
|
|
|
|
'''Get relative threshold in LUFS.
|
|
|
|
|
|
|
|
|
|
:param state: An instance of the :class:`R128State` class.
|
|
|
|
|
:type state: R128State
|
|
|
|
|
Args:
|
|
|
|
|
state (:class:`R128State`): An instance of the :class:`R128State`
|
|
|
|
|
class.
|
|
|
|
|
|
|
|
|
|
:raises ValueError: If Mode ``ModeI`` has not been set.
|
|
|
|
|
Raises:
|
|
|
|
|
ValueError: If ``MODE_I`` has not been set.
|
|
|
|
|
|
|
|
|
|
:return: The relative threshold in LUFS.
|
|
|
|
|
:rtype: float
|
|
|
|
|
Returns:
|
|
|
|
|
float: The relative threshold in LUFS.
|
|
|
|
|
'''
|
|
|
|
|
cdef double threshold
|
|
|
|
|
cdef int result
|
|
|
|
|
result = ebur128_relative_threshold(state._state, &threshold)
|
|
|
|
|
if result == ErrorCode.InvalidMode:
|
|
|
|
|
raise ValueError('Mode "ModeI" has not been set.')
|
|
|
|
|
if result == ErrorCode.INVALID_MODE:
|
|
|
|
|
raise ValueError('MODE_I has not been set.')
|
|
|
|
|
return threshold
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cpdef get_libebur128_version():
|
|
|
|
|
'''Gets the version number of the compiled libebur128.
|
|
|
|
|
|
|
|
|
|
:return: The major, minor, and patch numbers of the implemented libebur128
|
|
|
|
|
Returns:
|
|
|
|
|
str: The major, minor, and patch numbers of the implemented libebur128
|
|
|
|
|
version.
|
|
|
|
|
:rtype: tuple of int
|
|
|
|
|
'''
|
|
|
|
|
cdef int major, minor, patch
|
|
|
|
|
ebur128_get_version(&major, &minor, &patch)
|
|
|
|
|
return major, minor, patch
|
|
|
|
|
return '.'.join(map(str, (major, minor, patch)))
|
|
|
|
|