pax_global_header00006660000000000000000000000064126604102500014506gustar00rootroot0000000000000052 comment=1c52c7f28384f6496a95bd426d7e7738aa607049 libebur128-1.1.0/000077500000000000000000000000001266041025000133645ustar00rootroot00000000000000libebur128-1.1.0/.gitignore000066400000000000000000000000071266041025000153510ustar00rootroot00000000000000build/ libebur128-1.1.0/CMakeLists.txt000066400000000000000000000023411266041025000161240ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) project(libebur128 C) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}) include(utils) include(GNUInstallDirs) add_subdirectory(ebur128) add_subdirectory(test) to_yes_no(SUMMARY_HAS_QUEUE SUMMARY_SPEEXDSP_FOUND) to_yes_no(DISABLE_SPEEXDSP) if(ENABLE_INTERNAL_QUEUE_H) set(USE_QUEUE "using own copy of queue.h") else() set(USE_QUEUE "using system copy of queue.h") endif() ##### Print status message(STATUS "Status found / disabled --") message(STATUS "queue.h: ${SUMMARY_HAS_QUEUE}" " ${USE_QUEUE}") message(STATUS "speexdsp: ${SUMMARY_SPEEXDSP_FOUND}" " ${DISABLE_SPEEXDSP}") if(BUILD_STATIC_LIBS) message(STATUS "build static library and shared library!") else() message(STATUS "not building static library, set BUILD_STATIC_LIBS to ON to enable") endif() if(NOT SUMMARY_HAS_QUEUE AND NOT ENABLE_INTERNAL_QUEUE_H) message(FATAL_ERROR "queue.h not found, please set ENABLE_INTERNAL_QUEUE_H to ON") endif() if(ENABLE_TESTS) message(STATUS "building tests!") else() message(STATUS "not building tests, set ENABLE_TESTS to ON to enable") endif() libebur128-1.1.0/COPYING000066400000000000000000000020431266041025000144160ustar00rootroot00000000000000Copyright (c) 2011 Jan Kokemüller Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libebur128-1.1.0/README.md000066400000000000000000000027241266041025000146500ustar00rootroot00000000000000libebur128 ========== libebur128 is a library that implements the EBU R 128 standard for loudness normalisation. All source code is licensed under the MIT license. See COPYING file for details. See also [loudness-scanner tool](https://github.com/jiixyj/loudness-scanner). News ---- v1.1.0 released: * Add `ebur128_relative_threshold()` * Add channel definitions from ITU R-REC-BS 1770-4 to channel enum * Fix some minor build issues v1.0.3 released: * Fix build with recent speexdsp * Correct license file name * CMake option to disable static library * minimal-example.c: do not hard code program name in usage Features -------- * Portable ANSI C code * Implements M, S and I modes * Implements loudness range measurement (EBU - TECH 3342) * True peak scanning * Supports all samplerates by recalculation of the filter coefficients Requirements ------------ * [libspeexdsp](http://www.speex.org/) - Needed for `ebur128_true_peak`. Installation ------------ In the root folder, type: mkdir build cd build cmake .. make If you want the git version, run simply: git clone git://github.com/jiixyj/libebur128.git Usage ----- Library usage should be pretty straightforward. All exported symbols are documented in the ebur128.h header file. For a usage example, see minimal-example.c in the tests folder. On some operating systems, static libraries should be compiled as position independent code. You can enable that by turning on `WITH_STATIC_PIC`. libebur128-1.1.0/cmake/000077500000000000000000000000001266041025000144445ustar00rootroot00000000000000libebur128-1.1.0/cmake/utils.cmake000066400000000000000000000036111266041025000166070ustar00rootroot00000000000000macro(to_yes_no vars) foreach(var ${ARGV}) if(${var}) set(${var} "yes") else() set(${var} "no ") endif() endforeach() endmacro() macro(if_empty_print_missing vars) foreach(var ${ARGV}) if(NOT ${var}) set(${var} "") endif() endforeach() endmacro() function(to_space_list sc_list) set(ret) foreach(val ${${sc_list}}) set(ret "${ret} ${val}") endforeach() if(ret) string(STRIP ${ret} ret) set(${sc_list} "${ret}" PARENT_SCOPE) endif() endfunction() macro(find_pkg_config prefix pkgname) find_package(PkgConfig ${ARGV2}) if(PKG_CONFIG_FOUND) pkg_check_modules(${prefix}_PKGCONF ${ARGV2} ${pkgname}) if(${${prefix}_PKGCONF_FOUND}) message(STATUS "${pkgname} library dirs: ${${prefix}_PKGCONF_LIBRARY_DIRS}") message(STATUS "${pkgname} cflags: ${${prefix}_PKGCONF_CFLAGS_OTHER}") message(STATUS "${pkgname} include dirs: ${${prefix}_PKGCONF_INCLUDE_DIRS}") message(STATUS "${pkgname} libraries: ${${prefix}_PKGCONF_LIBRARIES}") message(STATUS "${pkgname} ldflags: ${${prefix}_PKGCONF_LDFLAGS_OTHER}") set(${prefix}_FOUND ${${prefix}_PKGCONF_FOUND}) set(${prefix}_CFLAGS ${${prefix}_PKGCONF_CFLAGS_OTHER}) to_space_list(${prefix}_CFLAGS) set(${prefix}_INCLUDE_DIRS ${${prefix}_PKGCONF_INCLUDE_DIRS}) foreach(lib ${${prefix}_PKGCONF_LIBRARIES}) string(TOUPPER ${lib} LIB) find_library(${prefix}_${LIB}_LIBRARY ${lib} HINTS ${${prefix}_PKGCONF_LIBRARY_DIRS}) mark_as_advanced(${prefix}_${LIB}_LIBRARY) list(APPEND ${prefix}_LIBRARIES ${${prefix}_${LIB}_LIBRARY}) endforeach() list(APPEND ${prefix}_LIBRARIES ${${prefix}_PKGCONF_LDFLAGS_OTHER}) endif() endif() endmacro() libebur128-1.1.0/doc/000077500000000000000000000000001266041025000141315ustar00rootroot00000000000000libebur128-1.1.0/doc/license/000077500000000000000000000000001266041025000155535ustar00rootroot00000000000000libebur128-1.1.0/doc/license/R128Scan.txt000066400000000000000000000021511266041025000175540ustar00rootroot00000000000000EBU R128 Gain processor. Copyright (C) 2011 Chris Moeller Portions copyright (c) 2011 Jan Kokemüller Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." libebur128-1.1.0/doc/license/queue.txt000066400000000000000000000027541266041025000174500ustar00rootroot00000000000000Copyright (c) 1991, 1993 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libebur128-1.1.0/ebur128/000077500000000000000000000000001266041025000145545ustar00rootroot00000000000000libebur128-1.1.0/ebur128/CMakeLists.txt000066400000000000000000000044111266041025000173140ustar00rootroot00000000000000set(BUILD_STATIC_LIBS ON CACHE BOOL "Build static library") set(WITH_STATIC_PIC OFF CACHE BOOL "Compile static library with -fPIC flag") set(ENABLE_INTERNAL_QUEUE_H OFF CACHE BOOL "Use own queue.h") set(DISABLE_SPEEXDSP OFF CACHE BOOL "Don't build with speexdsp") #### queue.h file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/queuetest.c "#include \nLIST_HEAD(listhead, entry) head;\nint main() { return 0; }") try_compile(HAS_QUEUE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/queuetest.c) set(SUMMARY_HAS_QUEUE ${HAS_QUEUE} CACHE INTERNAL "") if(ENABLE_INTERNAL_QUEUE_H) include_directories(SYSTEM queue) endif() if(MSVC) add_definitions(-D_USE_MATH_DEFINES) endif() set(EBUR128_VERSION_MAJOR 1) set(EBUR128_VERSION 1.1.0) #### static if(BUILD_STATIC_LIBS) add_library(ebur128_static STATIC ebur128.c) set_property(TARGET ebur128_static PROPERTY OUTPUT_NAME ebur128) endif() if(WITH_STATIC_PIC) set_property(TARGET ebur128_static PROPERTY POSITION_INDEPENDENT_CODE ON) endif() #### shared add_library(ebur128 SHARED ebur128.c) set_target_properties(ebur128 PROPERTIES SOVERSION ${EBUR128_VERSION_MAJOR} VERSION ${EBUR128_VERSION}) #### speexdsp if(NOT DISABLE_SPEEXDSP) find_pkg_config(SPEEXDSP speexdsp) if(SPEEXDSP_FOUND) target_include_directories(ebur128 PRIVATE ${SPEEXDSP_INCLUDE_DIRS}) target_compile_definitions(ebur128 PRIVATE -DUSE_SPEEX_RESAMPLER -DHAVE_STDINT_H) endif() endif() if(UNIX) target_link_libraries(ebur128 m) endif() if(SPEEXDSP_FOUND AND NOT DISABLE_SPEEXDSP) if(BUILD_STATIC_LIBS) target_compile_options(ebur128_static PRIVATE ${SPEEXDSP_CFLAGS}) endif() target_compile_options(ebur128 PRIVATE ${SPEEXDSP_CFLAGS}) target_link_libraries(ebur128 ${SPEEXDSP_LIBRARIES}) endif() set(SUMMARY_SPEEXDSP_FOUND ${SPEEXDSP_FOUND} CACHE INTERNAL "") set(EBUR128_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "") install(FILES ebur128.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) if(BUILD_STATIC_LIBS) install(TARGETS ebur128 ebur128_static DESTINATION ${CMAKE_INSTALL_LIBDIR}) else() install(TARGETS ebur128 DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() libebur128-1.1.0/ebur128/ebur128.c000066400000000000000000001133331266041025000161140ustar00rootroot00000000000000/* See COPYING file for copyright and license details. */ #include "ebur128.h" #include #include #include /* You may have to define _USE_MATH_DEFINES if you use MSVC */ #include #include /* This can be replaced by any BSD-like queue implementation. */ #include #ifdef USE_SPEEX_RESAMPLER #include #endif #define CHECK_ERROR(condition, errorcode, goto_point) \ if ((condition)) { \ errcode = (errorcode); \ goto goto_point; \ } SLIST_HEAD(ebur128_double_queue, ebur128_dq_entry); struct ebur128_dq_entry { double z; SLIST_ENTRY(ebur128_dq_entry) entries; }; struct ebur128_state_internal { /** Filtered audio data (used as ring buffer). */ double* audio_data; /** Size of audio_data array. */ size_t audio_data_frames; /** Current index for audio_data. */ size_t audio_data_index; /** How many frames are needed for a gating block. Will correspond to 400ms * of audio at initialization, and 100ms after the first block (75% overlap * as specified in the 2011 revision of BS1770). */ unsigned long needed_frames; /** The channel map. Has as many elements as there are channels. */ int* channel_map; /** How many samples fit in 100ms (rounded). */ unsigned long samples_in_100ms; /** BS.1770 filter coefficients (nominator). */ double b[5]; /** BS.1770 filter coefficients (denominator). */ double a[5]; /** BS.1770 filter state. */ double v[5][5]; /** Linked list of block energies. */ struct ebur128_double_queue block_list; /** Linked list of 3s-block energies, used to calculate LRA. */ struct ebur128_double_queue short_term_block_list; int use_histogram; unsigned long *block_energy_histogram; unsigned long *short_term_block_energy_histogram; /** Keeps track of when a new short term block is needed. */ size_t short_term_frame_counter; /** Maximum sample peak, one per channel */ double* sample_peak; /** Maximum true peak, one per channel */ double* true_peak; #ifdef USE_SPEEX_RESAMPLER SpeexResamplerState* resampler; #endif size_t oversample_factor; float* resampler_buffer_input; size_t resampler_buffer_input_frames; float* resampler_buffer_output; size_t resampler_buffer_output_frames; }; static double relative_gate = -10.0; /* Those will be calculated when initializing the library */ static double relative_gate_factor; static double minus_twenty_decibels; static double histogram_energies[1000]; static double histogram_energy_boundaries[1001]; static void ebur128_init_filter(ebur128_state* st) { int i, j; double f0 = 1681.974450955533; double G = 3.999843853973347; double Q = 0.7071752369554196; double K = tan(M_PI * f0 / (double) st->samplerate); double Vh = pow(10.0, G / 20.0); double Vb = pow(Vh, 0.4996667741545416); double pb[3] = {0.0, 0.0, 0.0}; double pa[3] = {1.0, 0.0, 0.0}; double rb[3] = {1.0, -2.0, 1.0}; double ra[3] = {1.0, 0.0, 0.0}; double a0 = 1.0 + K / Q + K * K ; pb[0] = (Vh + Vb * K / Q + K * K) / a0; pb[1] = 2.0 * (K * K - Vh) / a0; pb[2] = (Vh - Vb * K / Q + K * K) / a0; pa[1] = 2.0 * (K * K - 1.0) / a0; pa[2] = (1.0 - K / Q + K * K) / a0; /* fprintf(stderr, "%.14f %.14f %.14f %.14f %.14f\n", b1[0], b1[1], b1[2], a1[1], a1[2]); */ f0 = 38.13547087602444; Q = 0.5003270373238773; K = tan(M_PI * f0 / (double) st->samplerate); ra[1] = 2.0 * (K * K - 1.0) / (1.0 + K / Q + K * K); ra[2] = (1.0 - K / Q + K * K) / (1.0 + K / Q + K * K); /* fprintf(stderr, "%.14f %.14f\n", a2[1], a2[2]); */ st->d->b[0] = pb[0] * rb[0]; st->d->b[1] = pb[0] * rb[1] + pb[1] * rb[0]; st->d->b[2] = pb[0] * rb[2] + pb[1] * rb[1] + pb[2] * rb[0]; st->d->b[3] = pb[1] * rb[2] + pb[2] * rb[1]; st->d->b[4] = pb[2] * rb[2]; st->d->a[0] = pa[0] * ra[0]; st->d->a[1] = pa[0] * ra[1] + pa[1] * ra[0]; st->d->a[2] = pa[0] * ra[2] + pa[1] * ra[1] + pa[2] * ra[0]; st->d->a[3] = pa[1] * ra[2] + pa[2] * ra[1]; st->d->a[4] = pa[2] * ra[2]; for (i = 0; i < 5; ++i) { for (j = 0; j < 5; ++j) { st->d->v[i][j] = 0.0; } } } static int ebur128_init_channel_map(ebur128_state* st) { size_t i; st->d->channel_map = (int*) malloc(st->channels * sizeof(int)); if (!st->d->channel_map) return EBUR128_ERROR_NOMEM; if (st->channels == 4) { st->d->channel_map[0] = EBUR128_LEFT; st->d->channel_map[1] = EBUR128_RIGHT; st->d->channel_map[2] = EBUR128_LEFT_SURROUND; st->d->channel_map[3] = EBUR128_RIGHT_SURROUND; } else if (st->channels == 5) { st->d->channel_map[0] = EBUR128_LEFT; st->d->channel_map[1] = EBUR128_RIGHT; st->d->channel_map[2] = EBUR128_CENTER; st->d->channel_map[3] = EBUR128_LEFT_SURROUND; st->d->channel_map[4] = EBUR128_RIGHT_SURROUND; } else { for (i = 0; i < st->channels; ++i) { switch (i) { case 0: st->d->channel_map[i] = EBUR128_LEFT; break; case 1: st->d->channel_map[i] = EBUR128_RIGHT; break; case 2: st->d->channel_map[i] = EBUR128_CENTER; break; case 3: st->d->channel_map[i] = EBUR128_UNUSED; break; case 4: st->d->channel_map[i] = EBUR128_LEFT_SURROUND; break; case 5: st->d->channel_map[i] = EBUR128_RIGHT_SURROUND; break; default: st->d->channel_map[i] = EBUR128_UNUSED; break; } } } return EBUR128_SUCCESS; } #ifdef USE_SPEEX_RESAMPLER static int ebur128_init_resampler(ebur128_state* st) { int errcode = EBUR128_SUCCESS; if (st->samplerate < 96000) { st->d->oversample_factor = 4; } else if (st->samplerate < 192000) { st->d->oversample_factor = 2; } else { st->d->oversample_factor = 1; st->d->resampler_buffer_input = NULL; st->d->resampler_buffer_output = NULL; st->d->resampler = NULL; } st->d->resampler_buffer_input_frames = st->d->samples_in_100ms * 4; st->d->resampler_buffer_input = malloc(st->d->resampler_buffer_input_frames * st->channels * sizeof(float)); CHECK_ERROR(!st->d->resampler_buffer_input, EBUR128_ERROR_NOMEM, exit) st->d->resampler_buffer_output_frames = st->d->resampler_buffer_input_frames * st->d->oversample_factor; st->d->resampler_buffer_output = malloc (st->d->resampler_buffer_output_frames * st->channels * sizeof(float)); CHECK_ERROR(!st->d->resampler_buffer_output, EBUR128_ERROR_NOMEM, free_input) st->d->resampler = speex_resampler_init ((spx_uint32_t) st->channels, (spx_uint32_t) st->samplerate, (spx_uint32_t) (st->samplerate * st->d->oversample_factor), 8, NULL); CHECK_ERROR(!st->d->resampler, EBUR128_ERROR_NOMEM, free_output) return errcode; free_output: free(st->d->resampler_buffer_output); st->d->resampler_buffer_output = NULL; free_input: free(st->d->resampler_buffer_input); st->d->resampler_buffer_input = NULL; exit: return errcode; } static void ebur128_destroy_resampler(ebur128_state* st) { free(st->d->resampler_buffer_input); st->d->resampler_buffer_input = NULL; free(st->d->resampler_buffer_output); st->d->resampler_buffer_output = NULL; speex_resampler_destroy(st->d->resampler); st->d->resampler = NULL; } #endif void ebur128_get_version(int* major, int* minor, int* patch) { *major = EBUR128_VERSION_MAJOR; *minor = EBUR128_VERSION_MINOR; *patch = EBUR128_VERSION_PATCH; } ebur128_state* ebur128_init(unsigned int channels, unsigned long samplerate, int mode) { int errcode, result; ebur128_state* st; unsigned int i; st = (ebur128_state*) malloc(sizeof(ebur128_state)); CHECK_ERROR(!st, 0, exit) st->d = (struct ebur128_state_internal*) malloc(sizeof(struct ebur128_state_internal)); CHECK_ERROR(!st->d, 0, free_state) st->channels = channels; errcode = ebur128_init_channel_map(st); CHECK_ERROR(errcode, 0, free_internal) st->d->sample_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->sample_peak, 0, free_channel_map) st->d->true_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->true_peak, 0, free_sample_peak) for (i = 0; i < channels; ++i) { st->d->sample_peak[i] = 0.0; st->d->true_peak[i] = 0.0; } st->d->use_histogram = mode & EBUR128_MODE_HISTOGRAM ? 1 : 0; st->samplerate = samplerate; st->d->samples_in_100ms = (st->samplerate + 5) / 10; st->mode = mode; if ((mode & EBUR128_MODE_S) == EBUR128_MODE_S) { st->d->audio_data_frames = st->d->samples_in_100ms * 30; } else if ((mode & EBUR128_MODE_M) == EBUR128_MODE_M) { st->d->audio_data_frames = st->d->samples_in_100ms * 4; } else { goto free_true_peak; } st->d->audio_data = (double*) malloc(st->d->audio_data_frames * st->channels * sizeof(double)); CHECK_ERROR(!st->d->audio_data, 0, free_true_peak) ebur128_init_filter(st); if (st->d->use_histogram) { st->d->block_energy_histogram = malloc(1000 * sizeof(unsigned long)); CHECK_ERROR(!st->d->block_energy_histogram, 0, free_audio_data) for (i = 0; i < 1000; ++i) { st->d->block_energy_histogram[i] = 0; } } else { st->d->block_energy_histogram = NULL; } if (st->d->use_histogram) { st->d->short_term_block_energy_histogram = malloc(1000 * sizeof(unsigned long)); CHECK_ERROR(!st->d->short_term_block_energy_histogram, 0, free_block_energy_histogram) for (i = 0; i < 1000; ++i) { st->d->short_term_block_energy_histogram[i] = 0; } } else { st->d->short_term_block_energy_histogram = NULL; } SLIST_INIT(&st->d->block_list); SLIST_INIT(&st->d->short_term_block_list); st->d->short_term_frame_counter = 0; #ifdef USE_SPEEX_RESAMPLER result = ebur128_init_resampler(st); CHECK_ERROR(result, 0, free_short_term_block_energy_histogram) #endif /* the first block needs 400ms of audio data */ st->d->needed_frames = st->d->samples_in_100ms * 4; /* start at the beginning of the buffer */ st->d->audio_data_index = 0; /* initialize static constants */ relative_gate_factor = pow(10.0, relative_gate / 10.0); minus_twenty_decibels = pow(10.0, -20.0 / 10.0); histogram_energy_boundaries[0] = pow(10.0, (-70.0 + 0.691) / 10.0); if (st->d->use_histogram) { for (i = 0; i < 1000; ++i) { histogram_energies[i] = pow(10.0, ((double) i / 10.0 - 69.95 + 0.691) / 10.0); } for (i = 1; i < 1001; ++i) { histogram_energy_boundaries[i] = pow(10.0, ((double) i / 10.0 - 70.0 + 0.691) / 10.0); } } return st; free_short_term_block_energy_histogram: free(st->d->short_term_block_energy_histogram); free_block_energy_histogram: free(st->d->block_energy_histogram); free_audio_data: free(st->d->audio_data); free_true_peak: free(st->d->true_peak); free_sample_peak: free(st->d->sample_peak); free_channel_map: free(st->d->channel_map); free_internal: free(st->d); free_state: free(st); exit: return NULL; } void ebur128_destroy(ebur128_state** st) { struct ebur128_dq_entry* entry; free((*st)->d->block_energy_histogram); free((*st)->d->short_term_block_energy_histogram); free((*st)->d->audio_data); free((*st)->d->channel_map); free((*st)->d->sample_peak); free((*st)->d->true_peak); while (!SLIST_EMPTY(&(*st)->d->block_list)) { entry = SLIST_FIRST(&(*st)->d->block_list); SLIST_REMOVE_HEAD(&(*st)->d->block_list, entries); free(entry); } while (!SLIST_EMPTY(&(*st)->d->short_term_block_list)) { entry = SLIST_FIRST(&(*st)->d->short_term_block_list); SLIST_REMOVE_HEAD(&(*st)->d->short_term_block_list, entries); free(entry); } #ifdef USE_SPEEX_RESAMPLER ebur128_destroy_resampler(*st); #endif free((*st)->d); free(*st); *st = NULL; } static int ebur128_use_speex_resampler(ebur128_state* st) { #ifdef USE_SPEEX_RESAMPLER return ((st->mode & EBUR128_MODE_TRUE_PEAK) == EBUR128_MODE_TRUE_PEAK); #else (void) st; return 0; #endif } static void ebur128_check_true_peak(ebur128_state* st, size_t frames) { #ifdef USE_SPEEX_RESAMPLER size_t c, i; spx_uint32_t in_len = (spx_uint32_t) frames; spx_uint32_t out_len = (spx_uint32_t) st->d->resampler_buffer_output_frames; speex_resampler_process_interleaved_float( st->d->resampler, st->d->resampler_buffer_input, &in_len, st->d->resampler_buffer_output, &out_len); for (c = 0; c < st->channels; ++c) { for (i = 0; i < out_len; ++i) { if (st->d->resampler_buffer_output[i * st->channels + c] > st->d->true_peak[c]) { st->d->true_peak[c] = st->d->resampler_buffer_output[i * st->channels + c]; } else if (-st->d->resampler_buffer_output[i * st->channels + c] > st->d->true_peak[c]) { st->d->true_peak[c] = -st->d->resampler_buffer_output[i * st->channels + c]; } } } #else (void) st; (void) frames; #endif } #ifdef __SSE2_MATH__ #include #define TURN_ON_FTZ \ unsigned int mxcsr = _mm_getcsr(); \ _mm_setcsr(mxcsr | _MM_FLUSH_ZERO_ON); #define TURN_OFF_FTZ _mm_setcsr(mxcsr); #define FLUSH_MANUALLY #else #warning "manual FTZ is being used, please enable SSE2 (-msse2 -mfpmath=sse)" #define TURN_ON_FTZ #define TURN_OFF_FTZ #define FLUSH_MANUALLY \ st->d->v[ci][4] = fabs(st->d->v[ci][4]) < DBL_MIN ? 0.0 : st->d->v[ci][4]; \ st->d->v[ci][3] = fabs(st->d->v[ci][3]) < DBL_MIN ? 0.0 : st->d->v[ci][3]; \ st->d->v[ci][2] = fabs(st->d->v[ci][2]) < DBL_MIN ? 0.0 : st->d->v[ci][2]; \ st->d->v[ci][1] = fabs(st->d->v[ci][1]) < DBL_MIN ? 0.0 : st->d->v[ci][1]; #endif #define EBUR128_FILTER(type, min_scale, max_scale) \ static void ebur128_filter_##type(ebur128_state* st, const type* src, \ size_t frames) { \ static double scaling_factor = -((double) min_scale) > (double) max_scale ? \ -((double) min_scale) : (double) max_scale; \ double* audio_data = st->d->audio_data + st->d->audio_data_index; \ size_t i, c; \ \ TURN_ON_FTZ \ \ if ((st->mode & EBUR128_MODE_SAMPLE_PEAK) == EBUR128_MODE_SAMPLE_PEAK) { \ for (c = 0; c < st->channels; ++c) { \ double max = 0.0; \ for (i = 0; i < frames; ++i) { \ if (src[i * st->channels + c] > max) { \ max = src[i * st->channels + c]; \ } else if (-src[i * st->channels + c] > max) { \ max = -1.0 * src[i * st->channels + c]; \ } \ } \ max /= scaling_factor; \ if (max > st->d->sample_peak[c]) st->d->sample_peak[c] = max; \ } \ } \ if (ebur128_use_speex_resampler(st)) { \ for (c = 0; c < st->channels; ++c) { \ for (i = 0; i < frames; ++i) { \ st->d->resampler_buffer_input[i * st->channels + c] = \ (float) (src[i * st->channels + c] / scaling_factor); \ } \ } \ ebur128_check_true_peak(st, frames); \ } \ for (c = 0; c < st->channels; ++c) { \ int ci = st->d->channel_map[c] - 1; \ if (ci < 0) continue; \ else if (ci == EBUR128_DUAL_MONO - 1) ci = 0; /*dual mono */ \ for (i = 0; i < frames; ++i) { \ st->d->v[ci][0] = (double) (src[i * st->channels + c] / scaling_factor) \ - st->d->a[1] * st->d->v[ci][1] \ - st->d->a[2] * st->d->v[ci][2] \ - st->d->a[3] * st->d->v[ci][3] \ - st->d->a[4] * st->d->v[ci][4]; \ audio_data[i * st->channels + c] = \ st->d->b[0] * st->d->v[ci][0] \ + st->d->b[1] * st->d->v[ci][1] \ + st->d->b[2] * st->d->v[ci][2] \ + st->d->b[3] * st->d->v[ci][3] \ + st->d->b[4] * st->d->v[ci][4]; \ st->d->v[ci][4] = st->d->v[ci][3]; \ st->d->v[ci][3] = st->d->v[ci][2]; \ st->d->v[ci][2] = st->d->v[ci][1]; \ st->d->v[ci][1] = st->d->v[ci][0]; \ } \ FLUSH_MANUALLY \ } \ TURN_OFF_FTZ \ } EBUR128_FILTER(short, SHRT_MIN, SHRT_MAX) EBUR128_FILTER(int, INT_MIN, INT_MAX) EBUR128_FILTER(float, -1.0f, 1.0f) EBUR128_FILTER(double, -1.0, 1.0) static double ebur128_energy_to_loudness(double energy) { return 10 * (log(energy) / log(10.0)) - 0.691; } static size_t find_histogram_index(double energy) { size_t index_min = 0; size_t index_max = 1000; size_t index_mid; do { index_mid = (index_min + index_max) / 2; if (energy >= histogram_energy_boundaries[index_mid]) { index_min = index_mid; } else { index_max = index_mid; } } while (index_max - index_min != 1); return index_min; } static int ebur128_calc_gating_block(ebur128_state* st, size_t frames_per_block, double* optional_output) { size_t i, c; double sum = 0.0; double channel_sum; for (c = 0; c < st->channels; ++c) { if (st->d->channel_map[c] == EBUR128_UNUSED) continue; channel_sum = 0.0; if (st->d->audio_data_index < frames_per_block * st->channels) { for (i = 0; i < st->d->audio_data_index / st->channels; ++i) { channel_sum += st->d->audio_data[i * st->channels + c] * st->d->audio_data[i * st->channels + c]; } for (i = st->d->audio_data_frames - (frames_per_block - st->d->audio_data_index / st->channels); i < st->d->audio_data_frames; ++i) { channel_sum += st->d->audio_data[i * st->channels + c] * st->d->audio_data[i * st->channels + c]; } } else { for (i = st->d->audio_data_index / st->channels - frames_per_block; i < st->d->audio_data_index / st->channels; ++i) { channel_sum += st->d->audio_data[i * st->channels + c] * st->d->audio_data[i * st->channels + c]; } } if (st->d->channel_map[c] == EBUR128_Mp110 || st->d->channel_map[c] == EBUR128_Mm110 || st->d->channel_map[c] == EBUR128_Mp060 || st->d->channel_map[c] == EBUR128_Mm060 || st->d->channel_map[c] == EBUR128_Mp090 || st->d->channel_map[c] == EBUR128_Mm090) { channel_sum *= 1.41; } else if (st->d->channel_map[c] == EBUR128_DUAL_MONO) { channel_sum *= 2.0; } sum += channel_sum; } sum /= (double) frames_per_block; if (optional_output) { *optional_output = sum; return EBUR128_SUCCESS; } else if (sum >= histogram_energy_boundaries[0]) { if (st->d->use_histogram) { ++st->d->block_energy_histogram[find_histogram_index(sum)]; } else { struct ebur128_dq_entry* block; block = (struct ebur128_dq_entry*) malloc(sizeof(struct ebur128_dq_entry)); if (!block) return EBUR128_ERROR_NOMEM; block->z = sum; SLIST_INSERT_HEAD(&st->d->block_list, block, entries); } return EBUR128_SUCCESS; } else { return EBUR128_SUCCESS; } } int ebur128_set_channel(ebur128_state* st, unsigned int channel_number, int value) { if (channel_number >= st->channels) { return 1; } if (value == EBUR128_DUAL_MONO && (st->channels != 1 || channel_number != 0)) { fprintf(stderr, "EBUR128_DUAL_MONO only works with mono files!\n"); return 1; } st->d->channel_map[channel_number] = value; return 0; } int ebur128_change_parameters(ebur128_state* st, unsigned int channels, unsigned long samplerate) { int errcode; if (channels == st->channels && samplerate == st->samplerate) { return 2; } free(st->d->audio_data); st->d->audio_data = NULL; if (channels != st->channels) { unsigned int i; free(st->d->channel_map); st->d->channel_map = NULL; free(st->d->sample_peak); st->d->sample_peak = NULL; free(st->d->true_peak); st->d->true_peak = NULL; st->channels = channels; #ifdef USE_SPEEX_RESAMPLER ebur128_destroy_resampler(st); ebur128_init_resampler(st); #endif errcode = ebur128_init_channel_map(st); CHECK_ERROR(errcode, EBUR128_ERROR_NOMEM, exit) st->d->sample_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->sample_peak, EBUR128_ERROR_NOMEM, exit) st->d->true_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->true_peak, EBUR128_ERROR_NOMEM, exit) for (i = 0; i < channels; ++i) { st->d->sample_peak[i] = 0.0; st->d->true_peak[i] = 0.0; } } if (samplerate != st->samplerate) { st->samplerate = samplerate; ebur128_init_filter(st); } if ((st->mode & EBUR128_MODE_S) == EBUR128_MODE_S) { st->d->audio_data_frames = st->d->samples_in_100ms * 30; } else if ((st->mode & EBUR128_MODE_M) == EBUR128_MODE_M) { st->d->audio_data_frames = st->d->samples_in_100ms * 4; } else { return 1; } st->d->audio_data = (double*) malloc(st->d->audio_data_frames * st->channels * sizeof(double)); CHECK_ERROR(!st->d->audio_data, EBUR128_ERROR_NOMEM, exit) /* the first block needs 400ms of audio data */ st->d->needed_frames = st->d->samples_in_100ms * 4; /* start at the beginning of the buffer */ st->d->audio_data_index = 0; /* reset short term frame counter */ st->d->short_term_frame_counter = 0; return 0; exit: return 1; } static int ebur128_energy_shortterm(ebur128_state* st, double* out); #define EBUR128_ADD_FRAMES(type) \ int ebur128_add_frames_##type(ebur128_state* st, \ const type* src, size_t frames) { \ size_t src_index = 0; \ while (frames > 0) { \ if (frames >= st->d->needed_frames) { \ ebur128_filter_##type(st, src + src_index, st->d->needed_frames); \ src_index += st->d->needed_frames * st->channels; \ frames -= st->d->needed_frames; \ st->d->audio_data_index += st->d->needed_frames * st->channels; \ /* calculate the new gating block */ \ if ((st->mode & EBUR128_MODE_I) == EBUR128_MODE_I) { \ if (ebur128_calc_gating_block(st, st->d->samples_in_100ms * 4, NULL)) {\ return EBUR128_ERROR_NOMEM; \ } \ } \ if ((st->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA) { \ st->d->short_term_frame_counter += st->d->needed_frames; \ if (st->d->short_term_frame_counter == st->d->samples_in_100ms * 30) { \ struct ebur128_dq_entry* block; \ double st_energy; \ ebur128_energy_shortterm(st, &st_energy); \ if (st_energy >= histogram_energy_boundaries[0]) { \ if (st->d->use_histogram) { \ ++st->d->short_term_block_energy_histogram[ \ find_histogram_index(st_energy)];\ } else { \ block = (struct ebur128_dq_entry*) \ malloc(sizeof(struct ebur128_dq_entry)); \ if (!block) return EBUR128_ERROR_NOMEM; \ block->z = st_energy; \ SLIST_INSERT_HEAD(&st->d->short_term_block_list, block, entries);\ } \ } \ st->d->short_term_frame_counter = st->d->samples_in_100ms * 20; \ } \ } \ /* 100ms are needed for all blocks besides the first one */ \ st->d->needed_frames = st->d->samples_in_100ms; \ /* reset audio_data_index when buffer full */ \ if (st->d->audio_data_index == st->d->audio_data_frames * st->channels) {\ st->d->audio_data_index = 0; \ } \ } else { \ ebur128_filter_##type(st, src + src_index, frames); \ st->d->audio_data_index += frames * st->channels; \ if ((st->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA) { \ st->d->short_term_frame_counter += frames; \ } \ st->d->needed_frames -= frames; \ frames = 0; \ } \ } \ return EBUR128_SUCCESS; \ } EBUR128_ADD_FRAMES(short) EBUR128_ADD_FRAMES(int) EBUR128_ADD_FRAMES(float) EBUR128_ADD_FRAMES(double) static int ebur128_calc_relative_threshold(ebur128_state* st, size_t* above_thresh_counter, double* relative_threshold) { struct ebur128_dq_entry* it; size_t i; *relative_threshold = 0.0; *above_thresh_counter = 0; if (st->d->use_histogram) { for (i = 0; i < 1000; ++i) { *relative_threshold += st->d->block_energy_histogram[i] * histogram_energies[i]; *above_thresh_counter += st->d->block_energy_histogram[i]; } } else { SLIST_FOREACH(it, &st->d->block_list, entries) { ++*above_thresh_counter; *relative_threshold += it->z; } } if (*above_thresh_counter != 0) { *relative_threshold /= (double) *above_thresh_counter; *relative_threshold *= relative_gate_factor; } return EBUR128_SUCCESS; } static int ebur128_gated_loudness(ebur128_state** sts, size_t size, double* out) { struct ebur128_dq_entry* it; double gated_loudness = 0.0; double relative_threshold; size_t above_thresh_counter; size_t i, j, start_index; for (i = 0; i < size; i++) { if (sts[i] && (sts[i]->mode & EBUR128_MODE_I) != EBUR128_MODE_I) { return EBUR128_ERROR_INVALID_MODE; } } for (i = 0; i < size; i++) { if (!sts[i]) continue; ebur128_calc_relative_threshold(sts[i], &above_thresh_counter, &relative_threshold); } if (!above_thresh_counter) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } above_thresh_counter = 0; if (relative_threshold < histogram_energy_boundaries[0]) { start_index = 0; } else { start_index = find_histogram_index(relative_threshold); if (relative_threshold > histogram_energies[start_index]) { ++start_index; } } for (i = 0; i < size; i++) { if (!sts[i]) continue; if (sts[i]->d->use_histogram) { for (j = start_index; j < 1000; ++j) { gated_loudness += sts[i]->d->block_energy_histogram[j] * histogram_energies[j]; above_thresh_counter += sts[i]->d->block_energy_histogram[j]; } } else { SLIST_FOREACH(it, &sts[i]->d->block_list, entries) { if (it->z >= relative_threshold) { ++above_thresh_counter; gated_loudness += it->z; } } } } if (!above_thresh_counter) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } gated_loudness /= (double) above_thresh_counter; *out = ebur128_energy_to_loudness(gated_loudness); return EBUR128_SUCCESS; } int ebur128_relative_threshold(ebur128_state* st, double* out) { double relative_threshold; size_t above_thresh_counter; if (st && (st->mode & EBUR128_MODE_I) != EBUR128_MODE_I) return EBUR128_ERROR_INVALID_MODE; ebur128_calc_relative_threshold(st, &above_thresh_counter, &relative_threshold); if (!above_thresh_counter) { *out = -70.0; return EBUR128_SUCCESS; } *out = ebur128_energy_to_loudness(relative_threshold); return EBUR128_SUCCESS; } int ebur128_loudness_global(ebur128_state* st, double* out) { return ebur128_gated_loudness(&st, 1, out); } int ebur128_loudness_global_multiple(ebur128_state** sts, size_t size, double* out) { return ebur128_gated_loudness(sts, size, out); } static int ebur128_energy_in_interval(ebur128_state* st, size_t interval_frames, double* out) { if (interval_frames > st->d->audio_data_frames) { return EBUR128_ERROR_INVALID_MODE; } ebur128_calc_gating_block(st, interval_frames, out); return EBUR128_SUCCESS; } static int ebur128_energy_shortterm(ebur128_state* st, double* out) { return ebur128_energy_in_interval(st, st->d->samples_in_100ms * 30, out); } int ebur128_loudness_momentary(ebur128_state* st, double* out) { double energy; int error = ebur128_energy_in_interval(st, st->d->samples_in_100ms * 4, &energy); if (error) { return error; } else if (energy <= 0.0) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } *out = ebur128_energy_to_loudness(energy); return EBUR128_SUCCESS; } int ebur128_loudness_shortterm(ebur128_state* st, double* out) { double energy; int error = ebur128_energy_shortterm(st, &energy); if (error) { return error; } else if (energy <= 0.0) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } *out = ebur128_energy_to_loudness(energy); return EBUR128_SUCCESS; } static int ebur128_double_cmp(const void *p1, const void *p2) { const double* d1 = (const double*) p1; const double* d2 = (const double*) p2; return (*d1 > *d2) - (*d1 < *d2); } /* EBU - TECH 3342 */ int ebur128_loudness_range_multiple(ebur128_state** sts, size_t size, double* out) { size_t i, j; struct ebur128_dq_entry* it; double* stl_vector; size_t stl_size; double* stl_relgated; size_t stl_relgated_size; double stl_power, stl_integrated; /* High and low percentile energy */ double h_en, l_en; int use_histogram = 0; for (i = 0; i < size; ++i) { if (sts[i]) { if ((sts[i]->mode & EBUR128_MODE_LRA) != EBUR128_MODE_LRA) { return EBUR128_ERROR_INVALID_MODE; } if (i == 0 && sts[i]->mode & EBUR128_MODE_HISTOGRAM) { use_histogram = 1; } else if (use_histogram != !!(sts[i]->mode & EBUR128_MODE_HISTOGRAM)) { return EBUR128_ERROR_INVALID_MODE; } } } if (use_histogram) { unsigned long hist[1000] = { 0 }; size_t percentile_low, percentile_high; size_t index; stl_size = 0; stl_power = 0.0; for (i = 0; i < size; ++i) { if (!sts[i]) continue; for (j = 0; j < 1000; ++j) { hist[j] += sts[i]->d->short_term_block_energy_histogram[j]; stl_size += sts[i]->d->short_term_block_energy_histogram[j]; stl_power += sts[i]->d->short_term_block_energy_histogram[j] * histogram_energies[j]; } } if (!stl_size) { *out = 0.0; return EBUR128_SUCCESS; } stl_power /= stl_size; stl_integrated = minus_twenty_decibels * stl_power; if (stl_integrated < histogram_energy_boundaries[0]) { index = 0; } else { index = find_histogram_index(stl_integrated); if (stl_integrated > histogram_energies[index]) { ++index; } } stl_size = 0; for (j = index; j < 1000; ++j) { stl_size += hist[j]; } if (!stl_size) { *out = 0.0; return EBUR128_SUCCESS; } percentile_low = (size_t) ((stl_size - 1) * 0.1 + 0.5); percentile_high = (size_t) ((stl_size - 1) * 0.95 + 0.5); stl_size = 0; j = index; while (stl_size <= percentile_low) { stl_size += hist[j++]; } l_en = histogram_energies[j - 1]; while (stl_size <= percentile_high) { stl_size += hist[j++]; } h_en = histogram_energies[j - 1]; *out = ebur128_energy_to_loudness(h_en) - ebur128_energy_to_loudness(l_en); return EBUR128_SUCCESS; } else { stl_size = 0; for (i = 0; i < size; ++i) { if (!sts[i]) continue; SLIST_FOREACH(it, &sts[i]->d->short_term_block_list, entries) { ++stl_size; } } if (!stl_size) { *out = 0.0; return EBUR128_SUCCESS; } stl_vector = (double*) malloc(stl_size * sizeof(double)); if (!stl_vector) return EBUR128_ERROR_NOMEM; for (j = 0, i = 0; i < size; ++i) { if (!sts[i]) continue; SLIST_FOREACH(it, &sts[i]->d->short_term_block_list, entries) { stl_vector[j] = it->z; ++j; } } qsort(stl_vector, stl_size, sizeof(double), ebur128_double_cmp); stl_power = 0.0; for (i = 0; i < stl_size; ++i) { stl_power += stl_vector[i]; } stl_power /= (double) stl_size; stl_integrated = minus_twenty_decibels * stl_power; stl_relgated = stl_vector; stl_relgated_size = stl_size; while (stl_relgated_size > 0 && *stl_relgated < stl_integrated) { ++stl_relgated; --stl_relgated_size; } if (stl_relgated_size) { h_en = stl_relgated[(size_t) ((stl_relgated_size - 1) * 0.95 + 0.5)]; l_en = stl_relgated[(size_t) ((stl_relgated_size - 1) * 0.1 + 0.5)]; free(stl_vector); *out = ebur128_energy_to_loudness(h_en) - ebur128_energy_to_loudness(l_en); return EBUR128_SUCCESS; } else { free(stl_vector); *out = 0.0; return EBUR128_SUCCESS; } } } int ebur128_loudness_range(ebur128_state* st, double* out) { return ebur128_loudness_range_multiple(&st, 1, out); } int ebur128_sample_peak(ebur128_state* st, unsigned int channel_number, double* out) { if ((st->mode & EBUR128_MODE_SAMPLE_PEAK) != EBUR128_MODE_SAMPLE_PEAK) { return EBUR128_ERROR_INVALID_MODE; } else if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } *out = st->d->sample_peak[channel_number]; return EBUR128_SUCCESS; } #ifdef USE_SPEEX_RESAMPLER int ebur128_true_peak(ebur128_state* st, unsigned int channel_number, double* out) { if ((st->mode & EBUR128_MODE_TRUE_PEAK) != EBUR128_MODE_TRUE_PEAK) { return EBUR128_ERROR_INVALID_MODE; } else if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } *out = st->d->true_peak[channel_number] > st->d->sample_peak[channel_number] ? st->d->true_peak[channel_number] : st->d->sample_peak[channel_number]; return EBUR128_SUCCESS; } #endif libebur128-1.1.0/ebur128/ebur128.h000066400000000000000000000267531266041025000161320ustar00rootroot00000000000000/* See COPYING file for copyright and license details. */ #ifndef EBUR128_H_ #define EBUR128_H_ /** \file ebur128.h * \brief libebur128 - a library for loudness measurement according to * the EBU R128 standard. */ #ifdef __cplusplus extern "C" { #endif #define EBUR128_VERSION_MAJOR 1 #define EBUR128_VERSION_MINOR 1 #define EBUR128_VERSION_PATCH 0 #include /* for size_t */ /** \enum channel * Use these values when setting the channel map with ebur128_set_channel(). * See definitions in ITU R-REC-BS 1770-4 */ enum channel { EBUR128_UNUSED = 0, /**< unused channel (for example LFE channel) */ EBUR128_LEFT, EBUR128_Mp030 = 1, /**< itu M+030 */ EBUR128_RIGHT, EBUR128_Mm030 = 2, /**< itu M-030 */ EBUR128_CENTER, EBUR128_Mp000 = 3, /**< itu M+000 */ EBUR128_LEFT_SURROUND, EBUR128_Mp110 = 4, /**< itu M+110 */ EBUR128_RIGHT_SURROUND, EBUR128_Mm110 = 5, /**< itu M-110 */ EBUR128_DUAL_MONO, /**< a channel that is counted twice */ EBUR128_MpSC, /**< itu M+SC */ EBUR128_MmSC, /**< itu M-SC */ EBUR128_Mp060, /**< itu M+060 */ EBUR128_Mm060, /**< itu M-060 */ EBUR128_Mp090, /**< itu M+090 */ EBUR128_Mm090, /**< itu M-090 */ EBUR128_Mp135, /**< itu M+135 */ EBUR128_Mm135, /**< itu M-135 */ EBUR128_Mp180, /**< itu M+180 */ EBUR128_Up000, /**< itu U+000 */ EBUR128_Up030, /**< itu U+030 */ EBUR128_Um030, /**< itu U-030 */ EBUR128_Up045, /**< itu U+045 */ EBUR128_Um045, /**< itu U-030 */ EBUR128_Up090, /**< itu U+090 */ EBUR128_Um090, /**< itu U-090 */ EBUR128_Up110, /**< itu U+110 */ EBUR128_Um110, /**< itu U-110 */ EBUR128_Up135, /**< itu U+135 */ EBUR128_Um135, /**< itu U-135 */ EBUR128_Up180, /**< itu U+180 */ EBUR128_Tp000, /**< itu T+000 */ EBUR128_Bp000, /**< itu B+000 */ EBUR128_Bp045, /**< itu B+045 */ EBUR128_Bm045 /**< itu B-045 */ }; /** \enum error * Error return values. */ enum error { EBUR128_SUCCESS = 0, EBUR128_ERROR_NOMEM, EBUR128_ERROR_INVALID_MODE, EBUR128_ERROR_INVALID_CHANNEL_INDEX, EBUR128_ERROR_NO_CHANGE }; /** \enum mode * Use these values in ebur128_init (or'ed). Try to use the lowest possible * modes that suit your needs, as performance will be better. */ enum mode { /** can call ebur128_loudness_momentary */ EBUR128_MODE_M = (1 << 0), /** can call ebur128_loudness_shortterm */ EBUR128_MODE_S = (1 << 1) | EBUR128_MODE_M, /** can call ebur128_loudness_global_* and ebur128_relative_threshold */ EBUR128_MODE_I = (1 << 2) | EBUR128_MODE_M, /** can call ebur128_loudness_range */ EBUR128_MODE_LRA = (1 << 3) | EBUR128_MODE_S, /** can call ebur128_sample_peak */ EBUR128_MODE_SAMPLE_PEAK = (1 << 4) | EBUR128_MODE_M, /** can call ebur128_true_peak */ EBUR128_MODE_TRUE_PEAK = (1 << 5) | EBUR128_MODE_M | EBUR128_MODE_SAMPLE_PEAK, /** uses histogram algorithm to calculate loudness */ EBUR128_MODE_HISTOGRAM = (1 << 6) }; /** forward declaration of ebur128_state_internal */ struct ebur128_state_internal; /** \brief Contains information about the state of a loudness measurement. * * You should not need to modify this struct directly. */ typedef struct { int mode; /**< The current mode. */ unsigned int channels; /**< The number of channels. */ unsigned long samplerate; /**< The sample rate. */ struct ebur128_state_internal* d; /**< Internal state. */ } ebur128_state; /** \brief Get library version number. Do not pass null pointers here. * * @param major major version number of library * @param minor minor version number of library * @param patch patch version number of library */ void ebur128_get_version(int* major, int* minor, int* patch); /** \brief Initialize library state. * * @param channels the number of channels. * @param samplerate the sample rate. * @param mode see the mode enum for possible values. * @return an initialized library state. */ ebur128_state* ebur128_init(unsigned int channels, unsigned long samplerate, int mode); /** \brief Destroy library state. * * @param st pointer to a library state. */ void ebur128_destroy(ebur128_state** st); /** \brief Set channel type. * * The default is: * - 0 -> EBUR128_LEFT * - 1 -> EBUR128_RIGHT * - 2 -> EBUR128_CENTER * - 3 -> EBUR128_UNUSED * - 4 -> EBUR128_LEFT_SURROUND * - 5 -> EBUR128_RIGHT_SURROUND * * @param st library state. * @param channel_number zero based channel index. * @param value channel type from the "channel" enum. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_set_channel(ebur128_state* st, unsigned int channel_number, int value); /** \brief Change library parameters. * * Note that the channel map will be reset when setting a different number of * channels. The current unfinished block will be lost. * * @param st library state. * @param channels new number of channels. * @param samplerate new sample rate. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM on memory allocation error. The state will be * invalid and must be destroyed. * - EBUR128_ERROR_NO_CHANGE if channels and sample rate were not changed. */ int ebur128_change_parameters(ebur128_state* st, unsigned int channels, unsigned long samplerate); /** \brief Add frames to be processed. * * @param st library state. * @param src array of source frames. Channels must be interleaved. * @param frames number of frames. Not number of samples! * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM on memory allocation error. */ int ebur128_add_frames_short(ebur128_state* st, const short* src, size_t frames); /** \brief See \ref ebur128_add_frames_short */ int ebur128_add_frames_int(ebur128_state* st, const int* src, size_t frames); /** \brief See \ref ebur128_add_frames_short */ int ebur128_add_frames_float(ebur128_state* st, const float* src, size_t frames); /** \brief See \ref ebur128_add_frames_short */ int ebur128_add_frames_double(ebur128_state* st, const double* src, size_t frames); /** \brief Get global integrated loudness in LUFS. * * @param st library state. * @param out integrated loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not been set. */ int ebur128_loudness_global(ebur128_state* st, double* out); /** \brief Get global integrated loudness in LUFS across multiple instances. * * @param sts array of library states. * @param size length of sts * @param out integrated loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not been set. */ int ebur128_loudness_global_multiple(ebur128_state** sts, size_t size, double* out); /** \brief Get momentary loudness (last 400ms) in LUFS. * * @param st library state. * @param out momentary loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. */ int ebur128_loudness_momentary(ebur128_state* st, double* out); /** \brief Get short-term loudness (last 3s) in LUFS. * * @param st library state. * @param out short-term loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_S" has not been set. */ int ebur128_loudness_shortterm(ebur128_state* st, double* out); /** \brief Get loudness range (LRA) of programme in LU. * * Calculates loudness range according to EBU 3342. * * @param st library state. * @param out loudness range (LRA) in LU. Will not be changed in case of * error. EBUR128_ERROR_NOMEM or EBUR128_ERROR_INVALID_MODE will be * returned in this case. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM in case of memory allocation error. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_LRA" has not been set. */ int ebur128_loudness_range(ebur128_state* st, double* out); /** \brief Get loudness range (LRA) in LU across multiple instances. * * Calculates loudness range according to EBU 3342. * * @param sts array of library states. * @param size length of sts * @param out loudness range (LRA) in LU. Will not be changed in case of * error. EBUR128_ERROR_NOMEM or EBUR128_ERROR_INVALID_MODE will be * returned in this case. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM in case of memory allocation error. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_LRA" has not been set. */ int ebur128_loudness_range_multiple(ebur128_state** sts, size_t size, double* out); /** \brief Get maximum sample peak of selected channel in float format. * * @param st library state * @param channel_number channel to analyse * @param out maximum sample peak in float format (1.0 is 0 dBFS) * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_SAMPLE_PEAK" has not * been set. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_sample_peak(ebur128_state* st, unsigned int channel_number, double* out); /** \brief Get maximum true peak of selected channel in float format. * * Uses an implementation defined algorithm to calculate the true peak. Do not * try to compare resulting values across different versions of the library, * as the algorithm may change. * * The current implementation uses the Speex resampler with quality level 8 to * calculate true peak. Will oversample 4x for sample rates < 96000 Hz, 2x for * sample rates < 192000 Hz and leave the signal unchanged for 192000 Hz. * * @param st library state * @param channel_number channel to analyse * @param out maximum true peak in float format (1.0 is 0 dBFS) * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_TRUE_PEAK" has not * been set. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_true_peak(ebur128_state* st, unsigned int channel_number, double* out); /** \brief Get relative threshold in LUFS. * * @param st library state * @param out relative threshold in LUFS. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not * been set. */ int ebur128_relative_threshold(ebur128_state* st, double* out); #ifdef __cplusplus } #endif #endif /* EBUR128_H_ */ libebur128-1.1.0/ebur128/queue/000077500000000000000000000000001266041025000157005ustar00rootroot00000000000000libebur128-1.1.0/ebur128/queue/sys/000077500000000000000000000000001266041025000165165ustar00rootroot00000000000000libebur128-1.1.0/ebur128/queue/sys/queue.h000066400000000000000000000461231266041025000200210ustar00rootroot00000000000000/* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues, and circular queues. * * A singly-linked list is headed by a single forward pointer. The * elements are singly linked for minimum space and pointer manipulation * overhead at the expense of O(n) removal for arbitrary elements. New * elements can be added to the list after an existing element or at the * head of the list. Elements being removed from the head of the list * should use the explicit macro for this purpose for optimum * efficiency. A singly-linked list may only be traversed in the forward * direction. Singly-linked lists are ideal for applications with large * datasets and few or no removals or for implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List functions. */ #define LIST_INIT(head) do { \ (head)->lh_first = NULL; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (/*CONSTCOND*/0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ } while (/*CONSTCOND*/0) #define LIST_FOREACH(var, head, field) \ for ((var) = ((head)->lh_first); \ (var); \ (var) = ((var)->field.le_next)) /* * List access methods. */ #define LIST_EMPTY(head) ((head)->lh_first == NULL) #define LIST_FIRST(head) ((head)->lh_first) #define LIST_NEXT(elm, field) ((elm)->field.le_next) /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List functions. */ #define SLIST_INIT(head) do { \ (head)->slh_first = NULL; \ } while (/*CONSTCOND*/0) #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (/*CONSTCOND*/0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (/*CONSTCOND*/0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (/*CONSTCOND*/0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } \ else { \ struct type *curelm = (head)->slh_first; \ while(curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ } \ } while (/*CONSTCOND*/0) #define SLIST_FOREACH(var, head, field) \ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) /* * Singly-linked List access methods. */ #define SLIST_EMPTY(head) ((head)->slh_first == NULL) #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) /* * Singly-linked Tail queue declarations. */ #define STAILQ_HEAD(name, type) \ struct name { \ struct type *stqh_first; /* first element */ \ struct type **stqh_last; /* addr of last next element */ \ } #define STAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).stqh_first } #define STAILQ_ENTRY(type) \ struct { \ struct type *stqe_next; /* next element */ \ } /* * Singly-linked Tail queue functions. */ #define STAILQ_INIT(head) do { \ (head)->stqh_first = NULL; \ (head)->stqh_last = &(head)->stqh_first; \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ (head)->stqh_last = &(elm)->field.stqe_next; \ (head)->stqh_first = (elm); \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.stqe_next = NULL; \ *(head)->stqh_last = (elm); \ (head)->stqh_last = &(elm)->field.stqe_next; \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ (head)->stqh_last = &(elm)->field.stqe_next; \ (listelm)->field.stqe_next = (elm); \ } while (/*CONSTCOND*/0) #define STAILQ_REMOVE_HEAD(head, field) do { \ if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ (head)->stqh_last = &(head)->stqh_first; \ } while (/*CONSTCOND*/0) #define STAILQ_REMOVE(head, elm, type, field) do { \ if ((head)->stqh_first == (elm)) { \ STAILQ_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->stqh_first; \ while (curelm->field.stqe_next != (elm)) \ curelm = curelm->field.stqe_next; \ if ((curelm->field.stqe_next = \ curelm->field.stqe_next->field.stqe_next) == NULL) \ (head)->stqh_last = &(curelm)->field.stqe_next; \ } \ } while (/*CONSTCOND*/0) #define STAILQ_FOREACH(var, head, field) \ for ((var) = ((head)->stqh_first); \ (var); \ (var) = ((var)->field.stqe_next)) #define STAILQ_CONCAT(head1, head2) do { \ if (!STAILQ_EMPTY((head2))) { \ *(head1)->stqh_last = (head2)->stqh_first; \ (head1)->stqh_last = (head2)->stqh_last; \ STAILQ_INIT((head2)); \ } \ } while (/*CONSTCOND*/0) /* * Singly-linked Tail queue access methods. */ #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) #define STAILQ_FIRST(head) ((head)->stqh_first) #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (/*CONSTCOND*/0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_REMOVE(head, elm, type, field) do { \ if ((head)->sqh_first == (elm)) { \ SIMPLEQ_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->sqh_first; \ while (curelm->field.sqe_next != (elm)) \ curelm = curelm->field.sqe_next; \ if ((curelm->field.sqe_next = \ curelm->field.sqe_next->field.sqe_next) == NULL) \ (head)->sqh_last = &(curelm)->field.sqe_next; \ } \ } while (/*CONSTCOND*/0) #define SIMPLEQ_FOREACH(var, head, field) \ for ((var) = ((head)->sqh_first); \ (var); \ (var) = ((var)->field.sqe_next)) /* * Simple queue access methods. */ #define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) /* * Tail queue definitions. */ #define _TAILQ_HEAD(name, type, qual) \ struct name { \ qual type *tqh_first; /* first element */ \ qual type *qual *tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define _TAILQ_ENTRY(type, qual) \ struct { \ qual type *tqe_next; /* next element */ \ qual type *qual *tqe_prev; /* address of previous next element */\ } #define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_FOREACH(var, head, field) \ for ((var) = ((head)->tqh_first); \ (var); \ (var) = ((var)->field.tqe_next)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ (var); \ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) #define TAILQ_CONCAT(head1, head2, field) do { \ if (!TAILQ_EMPTY(head2)) { \ *(head1)->tqh_last = (head2)->tqh_first; \ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ (head1)->tqh_last = (head2)->tqh_last; \ TAILQ_INIT((head2)); \ } \ } while (/*CONSTCOND*/0) /* * Tail queue access methods. */ #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_HEAD_INITIALIZER(head) \ { (void *)&head, (void *)&head } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = (void *)(head); \ (head)->cqh_last = (void *)(head); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == (void *)(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == (void *)(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = (void *)(head); \ if ((head)->cqh_last == (void *)(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = (void *)(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == (void *)(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == (void *)(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == (void *)(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ } while (/*CONSTCOND*/0) #define CIRCLEQ_FOREACH(var, head, field) \ for ((var) = ((head)->cqh_first); \ (var) != (const void *)(head); \ (var) = ((var)->field.cqe_next)) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for ((var) = ((head)->cqh_last); \ (var) != (const void *)(head); \ (var) = ((var)->field.cqe_prev)) /* * Circular queue access methods. */ #define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) #define CIRCLEQ_LOOP_NEXT(head, elm, field) \ (((elm)->field.cqe_next == (void *)(head)) \ ? ((head)->cqh_first) \ : (elm->field.cqe_next)) #define CIRCLEQ_LOOP_PREV(head, elm, field) \ (((elm)->field.cqe_prev == (void *)(head)) \ ? ((head)->cqh_last) \ : (elm->field.cqe_prev)) #endif /* sys/queue.h */ libebur128-1.1.0/test/000077500000000000000000000000001266041025000143435ustar00rootroot00000000000000libebur128-1.1.0/test/CMakeLists.txt000066400000000000000000000013711266041025000171050ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.9) set(ENABLE_TESTS OFF CACHE BOOL "Build test binaries, needs libsndfile") if(ENABLE_TESTS) find_package(PkgConfig REQUIRED) find_pkg_config(SNDFILE sndfile REQUIRED) include_directories(${EBUR128_INCLUDE_DIR}) include_directories(SYSTEM ${SNDFILE_INCLUDE_DIRS}) add_executable(r128-test-library tests) add_executable(minimal-example minimal-example) set_property(TARGET r128-test-library APPEND_STRING PROPERTY COMPILE_FLAGS " ${SNDFILE_CFLAGS}") set_property(TARGET minimal-example APPEND_STRING PROPERTY COMPILE_FLAGS " ${SNDFILE_CFLAGS}") target_link_libraries(r128-test-library ebur128 ${SNDFILE_LIBRARIES}) target_link_libraries(minimal-example ebur128 ${SNDFILE_LIBRARIES}) endif() libebur128-1.1.0/test/minimal-example.c000066400000000000000000000036521266041025000175740ustar00rootroot00000000000000/* See COPYING file for copyright and license details. */ #include #include #include #include "ebur128.h" int main(int ac, const char* av[]) { SF_INFO file_info; SNDFILE* file; sf_count_t nr_frames_read; ebur128_state** sts = NULL; double* buffer; double loudness; int i; if (ac < 2) { fprintf(stderr, "usage: %s FILENAME...\n", av[0]); exit(1); } sts = malloc((size_t) (ac - 1) * sizeof(ebur128_state*)); for (i = 0; i < ac - 1; ++i) { memset(&file_info, '\0', sizeof(file_info)); file = sf_open(av[i + 1], SFM_READ, &file_info); sts[i] = ebur128_init((unsigned) file_info.channels, (unsigned) file_info.samplerate, EBUR128_MODE_I); /* example: set channel map (note: see ebur128.h for the default map) */ if (file_info.channels == 5) { ebur128_set_channel(sts[i], 0, EBUR128_LEFT); ebur128_set_channel(sts[i], 1, EBUR128_RIGHT); ebur128_set_channel(sts[i], 2, EBUR128_CENTER); ebur128_set_channel(sts[i], 3, EBUR128_LEFT_SURROUND); ebur128_set_channel(sts[i], 4, EBUR128_RIGHT_SURROUND); } buffer = (double*) malloc(sts[i]->samplerate * sts[i]->channels * sizeof(double)); while ((nr_frames_read = sf_readf_double(file, buffer, (sf_count_t) sts[i]->samplerate))) { ebur128_add_frames_double(sts[i], buffer, (size_t) nr_frames_read); } ebur128_loudness_global(sts[i], &loudness); fprintf(stderr, "%.2f LUFS, %s\n", loudness, av[i + 1]); free(buffer); buffer = NULL; if (sf_close(file)) { fprintf(stderr, "Could not close input file!\n"); } } ebur128_loudness_global_multiple(sts, (size_t) ac - 1, &loudness); fprintf(stderr, "-----------\n%.2f LUFS\n", loudness); /* clean up */ for (i = 0; i < ac - 1; ++i) { ebur128_destroy(&sts[i]); } free(sts); return 0; } libebur128-1.1.0/test/tests.c000066400000000000000000000161221266041025000156530ustar00rootroot00000000000000/* See COPYING file for copyright and license details. */ #include #include #include #include #include "ebur128.h" double test_global_loudness(const char* filename) { SF_INFO file_info; SNDFILE* file; sf_count_t nr_frames_read; ebur128_state* st = NULL; double gated_loudness; double* buffer; memset(&file_info, '\0', sizeof(file_info)); file = sf_open(filename, SFM_READ, &file_info); if (!file) { fprintf(stderr, "Could not open file %s!\n", filename); return 0.0; } st = ebur128_init((size_t) file_info.channels, (size_t) file_info.samplerate, EBUR128_MODE_I); if (file_info.channels == 5) { ebur128_set_channel(st, 0, EBUR128_LEFT); ebur128_set_channel(st, 1, EBUR128_RIGHT); ebur128_set_channel(st, 2, EBUR128_CENTER); ebur128_set_channel(st, 3, EBUR128_LEFT_SURROUND); ebur128_set_channel(st, 4, EBUR128_RIGHT_SURROUND); } buffer = (double*) malloc(st->samplerate * st->channels * sizeof(double)); while ((nr_frames_read = sf_readf_double(file, buffer, (sf_count_t) st->samplerate))) { ebur128_add_frames_double(st, buffer, (size_t) nr_frames_read); } ebur128_loudness_global(st, &gated_loudness); /* clean up */ ebur128_destroy(&st); free(buffer); buffer = NULL; if (sf_close(file)) { fprintf(stderr, "Could not close input file!\n"); } return gated_loudness; } double test_loudness_range(const char* filename) { SF_INFO file_info; SNDFILE* file; sf_count_t nr_frames_read; ebur128_state* st = NULL; double loudness_range; double* buffer; memset(&file_info, '\0', sizeof(file_info)); file = sf_open(filename, SFM_READ, &file_info); if (!file) { fprintf(stderr, "Could not open file %s!\n", filename); return 0.0; } st = ebur128_init((size_t) file_info.channels, (size_t) file_info.samplerate, EBUR128_MODE_LRA); if (file_info.channels == 5) { ebur128_set_channel(st, 0, EBUR128_LEFT); ebur128_set_channel(st, 1, EBUR128_RIGHT); ebur128_set_channel(st, 2, EBUR128_CENTER); ebur128_set_channel(st, 3, EBUR128_LEFT_SURROUND); ebur128_set_channel(st, 4, EBUR128_RIGHT_SURROUND); } buffer = (double*) malloc(st->samplerate * st->channels * sizeof(double)); while ((nr_frames_read = sf_readf_double(file, buffer, (sf_count_t) st->samplerate))) { ebur128_add_frames_double(st, buffer, (size_t) nr_frames_read); } ebur128_loudness_range(st, &loudness_range); /* clean up */ ebur128_destroy(&st); free(buffer); buffer = NULL; if (sf_close(file)) { fprintf(stderr, "Could not close input file!\n"); } return loudness_range; } double test_true_peak(const char* filename) { SF_INFO file_info; SNDFILE* file; sf_count_t nr_frames_read; ebur128_state* st = NULL; double true_peak; double* buffer; memset(&file_info, '\0', sizeof(file_info)); file = sf_open(filename, SFM_READ, &file_info); if (!file) { fprintf(stderr, "Could not open file %s!\n", filename); return 0.0; } st = ebur128_init((size_t) file_info.channels, (size_t) file_info.samplerate, EBUR128_MODE_LRA); if (file_info.channels == 5) { ebur128_set_channel(st, 0, EBUR128_LEFT); ebur128_set_channel(st, 1, EBUR128_RIGHT); ebur128_set_channel(st, 2, EBUR128_CENTER); ebur128_set_channel(st, 3, EBUR128_LEFT_SURROUND); ebur128_set_channel(st, 4, EBUR128_RIGHT_SURROUND); } buffer = (double*) malloc(st->samplerate * st->channels * sizeof(double)); while ((nr_frames_read = sf_readf_double(file, buffer, (sf_count_t) st->samplerate))) { ebur128_add_frames_double(st, buffer, (size_t) nr_frames_read); } ebur128_loudness_range(st, &true_peak); /* clean up */ ebur128_destroy(&st); free(buffer); buffer = NULL; if (sf_close(file)) { fprintf(stderr, "Could not close input file!\n"); } return true_peak; } double gr[] = {-23.0, -33.0, -23.0, -23.0, -23.0, -23.0, -23.0, -23.0, -23.0}; double gre[] = {-2.2953556442089987e+01, -3.2959860397340044e+01, -2.2995899818255047e+01, -2.3035918615414182e+01, -2.2949997446096436e+01, -2.3017157781104373e+01, -2.3017157781104373e+01, -2.2980242495081757e+01, -2.3009077718930545e+01}; double lra[] = {10.0, 5.0, 20.0, 15.0, 5.0, 15.0}; double lrae[] = {1.0001105488329134e+01, 4.9993734051522178e+00, 1.9995064067783115e+01, 1.4999273937723455e+01, 4.9747585878473721e+00, 1.4993650849123316e+01}; int main() { double result; fprintf(stderr, "Note: the tests do not have to pass with EXACT_PASSED.\n" "Passing these tests does not mean that the library is " "100%% EBU R 128 compliant!\n\n"); #define TEST_GLOBAL_LOUDNESS(filename, i) \ result = test_global_loudness(filename); \ if (result == result) { \ printf("%s, %s - %s: %1.16e\n", \ (result <= gr[i] + 0.1 && result >= gr[i] - 0.1) ? "PASSED" : "FAILED", \ (result == gre[i]) ? "EXACT_PASSED" : "EXACT_FAILED", \ filename, result); \ } TEST_GLOBAL_LOUDNESS("seq-3341-1-16bit.wav", 0) TEST_GLOBAL_LOUDNESS("seq-3341-2-16bit.wav", 1) TEST_GLOBAL_LOUDNESS("seq-3341-3-16bit-v02.wav", 2) TEST_GLOBAL_LOUDNESS("seq-3341-4-16bit-v02.wav", 3) TEST_GLOBAL_LOUDNESS("seq-3341-5-16bit-v02.wav", 4) TEST_GLOBAL_LOUDNESS("seq-3341-6-5channels-16bit.wav", 5) TEST_GLOBAL_LOUDNESS("seq-3341-6-6channels-WAVEEX-16bit.wav", 6) TEST_GLOBAL_LOUDNESS("seq-3341-7_seq-3342-5-24bit.wav", 7) TEST_GLOBAL_LOUDNESS("seq-3341-2011-8_seq-3342-6-24bit-v02.wav", 8) #define TEST_LRA(filename, i) \ result = test_loudness_range(filename); \ if (result == result) { \ printf("%s, %s - %s: %1.16e\n", \ (result <= lra[i] + 1 && result >= lra[i] - 1) ? "PASSED" : "FAILED", \ (result == lrae[i]) ? "EXACT_PASSED" : "EXACT_FAILED", \ filename, result); \ } TEST_LRA("seq-3342-1-16bit.wav", 0) TEST_LRA("seq-3342-2-16bit.wav", 1) TEST_LRA("seq-3342-3-16bit.wav", 2) TEST_LRA("seq-3342-4-16bit.wav", 3) TEST_LRA("seq-3341-7_seq-3342-5-24bit.wav", 4) TEST_LRA("seq-3341-2011-8_seq-3342-6-24bit-v02.wav", 5) return 0; }