filter_charcoal.o \
filter_dynamictext.o \
filter_invert.o \
+ filter_loudness.o \
filter_sepia.o \
producer_blipflash.o \
producer_count.o \
- transition_affine.o
+ transition_affine.o \
+ ebur128/ebur128.o
SRCS := $(OBJS:.o=.c)
--- /dev/null
+
+
+
+<!DOCTYPE html>
+<html>
+ <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# githubog: http://ogp.me/ns/fb/githubog#">
+ <meta charset='utf-8'>
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <title>libebur128/COPYING at master · jiixyj/libebur128</title>
+ <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub" />
+ <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub" />
+ <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-114.png" />
+ <link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114.png" />
+ <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-144.png" />
+ <link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144.png" />
+ <link rel="logo" type="image/svg" href="https://github-media-downloads.s3.amazonaws.com/github-logo.svg" />
+ <meta property="og:image" content="https://github.global.ssl.fastly.net/images/modules/logos_page/Octocat.png">
+ <meta name="hostname" content="github-fe130-cp1-prd.iad.github.net">
+ <meta name="ruby" content="ruby 2.1.0p0-github-tcmalloc (60139581e1) [x86_64-linux]">
+ <link rel="assets" href="https://github.global.ssl.fastly.net/">
+ <link rel="conduit-xhr" href="https://ghconduit.com:25035/">
+ <link rel="xhr-socket" href="/_sockets" />
+
+
+
+ <meta name="msapplication-TileImage" content="/windows-tile.png" />
+ <meta name="msapplication-TileColor" content="#ffffff" />
+ <meta name="selected-link" value="repo_source" data-pjax-transient />
+ <meta content="collector.githubapp.com" name="octolytics-host" /><meta content="collector-cdn.github.com" name="octolytics-script-host" /><meta content="github" name="octolytics-app-id" /><meta content="18DCB909:7D39:2D34A1:52DCA654" name="octolytics-dimension-request_id" /><meta content="821968" name="octolytics-actor-id" /><meta content="pez4brian" name="octolytics-actor-login" /><meta content="e1c69b58cbd1b68b0e7f93c351ab47e8bf2d3cfa957bb8a2265794bf05ce747d" name="octolytics-actor-hash" />
+
+
+
+
+ <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+
+ <meta content="authenticity_token" name="csrf-param" />
+<meta content="bUsJKeNDBdW6sL3g5ojzZJ5XksGcrDgGRp2LGt9IVyo=" name="csrf-token" />
+
+ <link href="https://github.global.ssl.fastly.net/assets/github-afd65da2802beafd8aee40df66e8b576092b2913.css" media="all" rel="stylesheet" type="text/css" />
+ <link href="https://github.global.ssl.fastly.net/assets/github2-3a909621ac79c89e7f414c6a6babc7f449d8ee93.css" media="all" rel="stylesheet" type="text/css" />
+
+
+
+ <script src="https://github.global.ssl.fastly.net/assets/frameworks-bf5987648bb83690ac0a5e955f74bbaf6ba44c4a.js" type="text/javascript"></script>
+ <script src="https://github.global.ssl.fastly.net/assets/github-8f0f971134413bf7449fde4428a2d0683d647ca5.js" type="text/javascript"></script>
+
+ <meta http-equiv="x-pjax-version" content="5fe7ec139c1bfc53297d312484aaf0e3">
+
+ <link data-pjax-transient rel='permalink' href='/jiixyj/libebur128/blob/12145473831324d3b72b490f34c3d8993ba69b44/COPYING'>
+ <meta property="og:title" content="libebur128"/>
+ <meta property="og:type" content="githubog:gitrepository"/>
+ <meta property="og:url" content="https://github.com/jiixyj/libebur128"/>
+ <meta property="og:image" content="https://github.global.ssl.fastly.net/images/gravatars/gravatar-user-420.png"/>
+ <meta property="og:site_name" content="GitHub"/>
+ <meta property="og:description" content="libebur128 - A library implementing the EBU R128 loudness standard."/>
+
+ <meta name="description" content="libebur128 - A library implementing the EBU R128 loudness standard." />
+
+ <meta content="491153" name="octolytics-dimension-user_id" /><meta content="jiixyj" name="octolytics-dimension-user_login" /><meta content="1641727" name="octolytics-dimension-repository_id" /><meta content="jiixyj/libebur128" name="octolytics-dimension-repository_nwo" /><meta content="true" name="octolytics-dimension-repository_public" /><meta content="false" name="octolytics-dimension-repository_is_fork" /><meta content="1641727" name="octolytics-dimension-repository_network_root_id" /><meta content="jiixyj/libebur128" name="octolytics-dimension-repository_network_root_nwo" />
+ <link href="https://github.com/jiixyj/libebur128/commits/master.atom" rel="alternate" title="Recent Commits to libebur128:master" type="application/atom+xml" />
+
+ </head>
+
+
+ <body class="logged_in env-production linux vis-public page-blob">
+ <div class="wrapper">
+
+
+
+
+
+
+ <div class="header header-logged-in true">
+ <div class="container clearfix">
+
+ <a class="header-logo-invertocat" href="https://github.com/">
+ <span class="mega-octicon octicon-mark-github"></span>
+</a>
+
+
+ <a href="/notifications" class="notification-indicator tooltipped downwards" data-gotokey="n" title="You have unread notifications">
+ <span class="mail-status unread"></span>
+</a>
+
+ <div class="command-bar js-command-bar in-repository">
+ <form accept-charset="UTF-8" action="/search" class="command-bar-form" id="top_search_form" method="get">
+
+<input type="text" data-hotkey=" s" name="q" id="js-command-bar-field" placeholder="Search or type a command" tabindex="1" autocapitalize="off"
+
+ data-username="pez4brian"
+ data-repo="jiixyj/libebur128"
+ data-branch="master"
+ data-sha="2071a1c55439f7ecbcae0bf7118d52c3f9aa0b57"
+ >
+
+ <input type="hidden" name="nwo" value="jiixyj/libebur128" />
+
+ <div class="select-menu js-menu-container js-select-menu search-context-select-menu">
+ <span class="minibutton select-menu-button js-menu-target">
+ <span class="js-select-button">This repository</span>
+ </span>
+
+ <div class="select-menu-modal-holder js-menu-content js-navigation-container">
+ <div class="select-menu-modal">
+
+ <div class="select-menu-item js-navigation-item js-this-repository-navigation-item selected">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <input type="radio" class="js-search-this-repository" name="search_target" value="repository" checked="checked" />
+ <div class="select-menu-item-text js-select-button-text">This repository</div>
+ </div> <!-- /.select-menu-item -->
+
+ <div class="select-menu-item js-navigation-item js-all-repositories-navigation-item">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <input type="radio" name="search_target" value="global" />
+ <div class="select-menu-item-text js-select-button-text">All repositories</div>
+ </div> <!-- /.select-menu-item -->
+
+ </div>
+ </div>
+ </div>
+
+ <span class="octicon help tooltipped downwards" title="Show command bar help">
+ <span class="octicon octicon-question"></span>
+ </span>
+
+
+ <input type="hidden" name="ref" value="cmdform">
+
+</form>
+ <ul class="top-nav">
+ <li class="explore"><a href="/explore">Explore</a></li>
+ <li><a href="https://gist.github.com">Gist</a></li>
+ <li><a href="/blog">Blog</a></li>
+ <li><a href="https://help.github.com">Help</a></li>
+ </ul>
+ </div>
+
+
+
+
+ <ul id="user-links">
+ <li>
+ <a href="/pez4brian" class="name">
+ <img height="20" src="https://2.gravatar.com/avatar/55f78d3c922861ef9496c88beb291499?d=https%3A%2F%2Fidenticons.github.com%2Faf7938a8dd1620e54ac18e1f164014ec.png&r=x&s=140" width="20" /> pez4brian
+ </a>
+ </li>
+
+ <li class="new-menu dropdown-toggle js-menu-container">
+ <a href="#" class="js-menu-target tooltipped downwards" title="Create new…">
+ <span class="octicon octicon-plus"></span>
+ <span class="dropdown-arrow"></span>
+ </a>
+
+ <div class="js-menu-content">
+ </div>
+ </li>
+
+ <li>
+ <a href="/settings/profile" id="account_settings"
+ class="tooltipped downwards"
+ aria-label="Account settings "
+ title="Account settings ">
+ <span class="octicon octicon-tools"></span>
+ </a>
+ </li>
+ <li>
+ <a class="tooltipped downwards" href="/logout" data-method="post" id="logout" title="Sign out" aria-label="Sign out">
+ <span class="octicon octicon-log-out"></span>
+ </a>
+ </li>
+
+ </ul>
+
+<div class="js-new-dropdown-contents hidden">
+
+
+<ul class="dropdown-menu">
+ <li>
+ <a href="/new"><span class="octicon octicon-repo-create"></span> New repository</a>
+ </li>
+ <li>
+ <a href="/organizations/new"><span class="octicon octicon-organization"></span> New organization</a>
+ </li>
+
+
+
+ <li class="section-title">
+ <span title="jiixyj/libebur128">This repository</span>
+ </li>
+ <li>
+ <a href="/jiixyj/libebur128/issues/new"><span class="octicon octicon-issue-opened"></span> New issue</a>
+ </li>
+</ul>
+
+</div>
+
+
+
+ </div>
+</div>
+
+
+
+
+
+
+
+
+ <div class="site" itemscope itemtype="http://schema.org/WebPage">
+
+ <div class="pagehead repohead instapaper_ignore readability-menu">
+ <div class="container">
+
+
+<ul class="pagehead-actions">
+
+ <li class="subscription">
+ <form accept-charset="UTF-8" action="/notifications/subscribe" class="js-social-container" data-autosubmit="true" data-remote="true" method="post"><div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="bUsJKeNDBdW6sL3g5ojzZJ5XksGcrDgGRp2LGt9IVyo=" /></div> <input id="repository_id" name="repository_id" type="hidden" value="1641727" />
+
+ <div class="select-menu js-menu-container js-select-menu">
+ <a class="social-count js-social-count" href="/jiixyj/libebur128/watchers">
+ 14
+ </a>
+ <span class="minibutton select-menu-button with-count js-menu-target" role="button" tabindex="0">
+ <span class="js-select-button">
+ <span class="octicon octicon-eye-watch"></span>
+ Watch
+ </span>
+ </span>
+
+ <div class="select-menu-modal-holder">
+ <div class="select-menu-modal subscription-menu-modal js-menu-content">
+ <div class="select-menu-header">
+ <span class="select-menu-title">Notification status</span>
+ <span class="octicon octicon-remove-close js-menu-close"></span>
+ </div> <!-- /.select-menu-header -->
+
+ <div class="select-menu-list js-navigation-container" role="menu">
+
+ <div class="select-menu-item js-navigation-item selected" role="menuitem" tabindex="0">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <div class="select-menu-item-text">
+ <input checked="checked" id="do_included" name="do" type="radio" value="included" />
+ <h4>Not watching</h4>
+ <span class="description">You only receive notifications for conversations in which you participate or are @mentioned.</span>
+ <span class="js-select-button-text hidden-select-button-text">
+ <span class="octicon octicon-eye-watch"></span>
+ Watch
+ </span>
+ </div>
+ </div> <!-- /.select-menu-item -->
+
+ <div class="select-menu-item js-navigation-item " role="menuitem" tabindex="0">
+ <span class="select-menu-item-icon octicon octicon octicon-check"></span>
+ <div class="select-menu-item-text">
+ <input id="do_subscribed" name="do" type="radio" value="subscribed" />
+ <h4>Watching</h4>
+ <span class="description">You receive notifications for all conversations in this repository.</span>
+ <span class="js-select-button-text hidden-select-button-text">
+ <span class="octicon octicon-eye-unwatch"></span>
+ Unwatch
+ </span>
+ </div>
+ </div> <!-- /.select-menu-item -->
+
+ <div class="select-menu-item js-navigation-item " role="menuitem" tabindex="0">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <div class="select-menu-item-text">
+ <input id="do_ignore" name="do" type="radio" value="ignore" />
+ <h4>Ignoring</h4>
+ <span class="description">You do not receive any notifications for conversations in this repository.</span>
+ <span class="js-select-button-text hidden-select-button-text">
+ <span class="octicon octicon-mute"></span>
+ Stop ignoring
+ </span>
+ </div>
+ </div> <!-- /.select-menu-item -->
+
+ </div> <!-- /.select-menu-list -->
+
+ </div> <!-- /.select-menu-modal -->
+ </div> <!-- /.select-menu-modal-holder -->
+ </div> <!-- /.select-menu -->
+
+</form>
+ </li>
+
+ <li>
+
+
+ <div class="js-toggler-container js-social-container starring-container ">
+ <a href="/jiixyj/libebur128/unstar"
+ class="minibutton with-count js-toggler-target star-button starred upwards"
+ title="Unstar this repository" data-remote="true" data-method="post" rel="nofollow">
+ <span class="octicon octicon-star-delete"></span><span class="text">Unstar</span>
+ </a>
+
+ <a href="/jiixyj/libebur128/star"
+ class="minibutton with-count js-toggler-target star-button unstarred upwards"
+ title="Star this repository" data-remote="true" data-method="post" rel="nofollow">
+ <span class="octicon octicon-star"></span><span class="text">Star</span>
+ </a>
+
+ <a class="social-count js-social-count" href="/jiixyj/libebur128/stargazers">
+ 36
+ </a>
+ </div>
+
+ </li>
+
+
+ <li>
+ <a href="/jiixyj/libebur128/fork" class="minibutton with-count js-toggler-target fork-button lighter upwards" title="Fork this repo" rel="facebox nofollow">
+ <span class="octicon octicon-git-branch-create"></span><span class="text">Fork</span>
+ </a>
+ <a href="/jiixyj/libebur128/network" class="social-count">5</a>
+ </li>
+
+
+</ul>
+
+ <h1 itemscope itemtype="http://data-vocabulary.org/Breadcrumb" class="entry-title public">
+ <span class="repo-label"><span>public</span></span>
+ <span class="mega-octicon octicon-repo"></span>
+ <span class="author">
+ <a href="/jiixyj" class="url fn" itemprop="url" rel="author"><span itemprop="title">jiixyj</span></a>
+ </span>
+ <span class="repohead-name-divider">/</span>
+ <strong><a href="/jiixyj/libebur128" class="js-current-repository js-repo-home-link">libebur128</a></strong>
+
+ <span class="page-context-loader">
+ <img alt="Octocat-spinner-32" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
+ </span>
+
+ </h1>
+ </div><!-- /.container -->
+ </div><!-- /.repohead -->
+
+ <div class="container">
+
+
+ <div class="repository-with-sidebar repo-container ">
+
+ <div class="repository-sidebar">
+
+
+<div class="sunken-menu vertical-right repo-nav js-repo-nav js-repository-container-pjax js-octicon-loaders">
+ <div class="sunken-menu-contents">
+ <ul class="sunken-menu-group">
+ <li class="tooltipped leftwards" title="Code">
+ <a href="/jiixyj/libebur128" aria-label="Code" class="selected js-selected-navigation-item sunken-menu-item" data-gotokey="c" data-pjax="true" data-selected-links="repo_source repo_downloads repo_commits repo_tags repo_branches /jiixyj/libebur128">
+ <span class="octicon octicon-code"></span> <span class="full-word">Code</span>
+ <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
+</a> </li>
+
+ <li class="tooltipped leftwards" title="Issues">
+ <a href="/jiixyj/libebur128/issues" aria-label="Issues" class="js-selected-navigation-item sunken-menu-item js-disable-pjax" data-gotokey="i" data-selected-links="repo_issues /jiixyj/libebur128/issues">
+ <span class="octicon octicon-issue-opened"></span> <span class="full-word">Issues</span>
+ <span class='counter'>4</span>
+ <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
+</a> </li>
+
+ <li class="tooltipped leftwards" title="Pull Requests">
+ <a href="/jiixyj/libebur128/pulls" aria-label="Pull Requests" class="js-selected-navigation-item sunken-menu-item js-disable-pjax" data-gotokey="p" data-selected-links="repo_pulls /jiixyj/libebur128/pulls">
+ <span class="octicon octicon-git-pull-request"></span> <span class="full-word">Pull Requests</span>
+ <span class='counter'>0</span>
+ <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
+</a> </li>
+
+
+ <li class="tooltipped leftwards" title="Wiki">
+ <a href="/jiixyj/libebur128/wiki" aria-label="Wiki" class="js-selected-navigation-item sunken-menu-item" data-pjax="true" data-selected-links="repo_wiki /jiixyj/libebur128/wiki">
+ <span class="octicon octicon-book"></span> <span class="full-word">Wiki</span>
+ <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
+</a> </li>
+ </ul>
+ <div class="sunken-menu-separator"></div>
+ <ul class="sunken-menu-group">
+
+ <li class="tooltipped leftwards" title="Pulse">
+ <a href="/jiixyj/libebur128/pulse" aria-label="Pulse" class="js-selected-navigation-item sunken-menu-item" data-pjax="true" data-selected-links="pulse /jiixyj/libebur128/pulse">
+ <span class="octicon octicon-pulse"></span> <span class="full-word">Pulse</span>
+ <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
+</a> </li>
+
+ <li class="tooltipped leftwards" title="Graphs">
+ <a href="/jiixyj/libebur128/graphs" aria-label="Graphs" class="js-selected-navigation-item sunken-menu-item" data-pjax="true" data-selected-links="repo_graphs repo_contributors /jiixyj/libebur128/graphs">
+ <span class="octicon octicon-graph"></span> <span class="full-word">Graphs</span>
+ <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
+</a> </li>
+
+ <li class="tooltipped leftwards" title="Network">
+ <a href="/jiixyj/libebur128/network" aria-label="Network" class="js-selected-navigation-item sunken-menu-item js-disable-pjax" data-selected-links="repo_network /jiixyj/libebur128/network">
+ <span class="octicon octicon-git-branch"></span> <span class="full-word">Network</span>
+ <img alt="Octocat-spinner-32" class="mini-loader" height="16" src="https://github.global.ssl.fastly.net/images/spinners/octocat-spinner-32.gif" width="16" />
+</a> </li>
+ </ul>
+
+
+ </div>
+</div>
+
+ <div class="only-with-full-nav">
+
+
+
+
+<div class="clone-url open"
+ data-protocol-type="http"
+ data-url="/users/set_protocol?protocol_selector=http&protocol_type=clone">
+ <h3><strong>HTTPS</strong> clone URL</h3>
+ <div class="clone-url-box">
+ <input type="text" class="clone js-url-field"
+ value="https://github.com/jiixyj/libebur128.git" readonly="readonly">
+
+ <span class="js-zeroclipboard url-box-clippy minibutton zeroclipboard-button" data-clipboard-text="https://github.com/jiixyj/libebur128.git" data-copied-hint="copied!" title="copy to clipboard"><span class="octicon octicon-clippy"></span></span>
+ </div>
+</div>
+
+
+
+<div class="clone-url "
+ data-protocol-type="ssh"
+ data-url="/users/set_protocol?protocol_selector=ssh&protocol_type=clone">
+ <h3><strong>SSH</strong> clone URL</h3>
+ <div class="clone-url-box">
+ <input type="text" class="clone js-url-field"
+ value="git@github.com:jiixyj/libebur128.git" readonly="readonly">
+
+ <span class="js-zeroclipboard url-box-clippy minibutton zeroclipboard-button" data-clipboard-text="git@github.com:jiixyj/libebur128.git" data-copied-hint="copied!" title="copy to clipboard"><span class="octicon octicon-clippy"></span></span>
+ </div>
+</div>
+
+
+
+<div class="clone-url "
+ data-protocol-type="subversion"
+ data-url="/users/set_protocol?protocol_selector=subversion&protocol_type=clone">
+ <h3><strong>Subversion</strong> checkout URL</h3>
+ <div class="clone-url-box">
+ <input type="text" class="clone js-url-field"
+ value="https://github.com/jiixyj/libebur128" readonly="readonly">
+
+ <span class="js-zeroclipboard url-box-clippy minibutton zeroclipboard-button" data-clipboard-text="https://github.com/jiixyj/libebur128" data-copied-hint="copied!" title="copy to clipboard"><span class="octicon octicon-clippy"></span></span>
+ </div>
+</div>
+
+
+<p class="clone-options">You can clone with
+ <a href="#" class="js-clone-selector" data-protocol="http">HTTPS</a>,
+ <a href="#" class="js-clone-selector" data-protocol="ssh">SSH</a>,
+ or <a href="#" class="js-clone-selector" data-protocol="subversion">Subversion</a>.
+ <span class="octicon help tooltipped upwards" title="Get help on which URL is right for you.">
+ <a href="https://help.github.com/articles/which-remote-url-should-i-use">
+ <span class="octicon octicon-question"></span>
+ </a>
+ </span>
+</p>
+
+
+
+ <a href="/jiixyj/libebur128/archive/master.zip"
+ class="minibutton sidebar-button"
+ title="Download this repository as a zip file"
+ rel="nofollow">
+ <span class="octicon octicon-cloud-download"></span>
+ Download ZIP
+ </a>
+ </div>
+ </div><!-- /.repository-sidebar -->
+
+ <div id="js-repo-pjax-container" class="repository-content context-loader-container" data-pjax-container>
+
+
+
+<!-- blob contrib key: blob_contributors:v21:91171c652a3664ed7857f22a8d4032fc -->
+
+<p title="This is a placeholder element" class="js-history-link-replace hidden"></p>
+
+<a href="/jiixyj/libebur128/find/master" data-pjax data-hotkey="t" class="js-show-file-finder" style="display:none">Show File Finder</a>
+
+<div class="file-navigation">
+
+
+<div class="select-menu js-menu-container js-select-menu" >
+ <span class="minibutton select-menu-button js-menu-target" data-hotkey="w"
+ data-master-branch="master"
+ data-ref="master"
+ role="button" aria-label="Switch branches or tags" tabindex="0">
+ <span class="octicon octicon-git-branch"></span>
+ <i>branch:</i>
+ <span class="js-select-button">master</span>
+ </span>
+
+ <div class="select-menu-modal-holder js-menu-content js-navigation-container" data-pjax>
+
+ <div class="select-menu-modal">
+ <div class="select-menu-header">
+ <span class="select-menu-title">Switch branches/tags</span>
+ <span class="octicon octicon-remove-close js-menu-close"></span>
+ </div> <!-- /.select-menu-header -->
+
+ <div class="select-menu-filters">
+ <div class="select-menu-text-filter">
+ <input type="text" aria-label="Filter branches/tags" id="context-commitish-filter-field" class="js-filterable-field js-navigation-enable" placeholder="Filter branches/tags">
+ </div>
+ <div class="select-menu-tabs">
+ <ul>
+ <li class="select-menu-tab">
+ <a href="#" data-tab-filter="branches" class="js-select-menu-tab">Branches</a>
+ </li>
+ <li class="select-menu-tab">
+ <a href="#" data-tab-filter="tags" class="js-select-menu-tab">Tags</a>
+ </li>
+ </ul>
+ </div><!-- /.select-menu-tabs -->
+ </div><!-- /.select-menu-filters -->
+
+ <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-tab-filter="branches">
+
+ <div data-filterable-for="context-commitish-filter-field" data-filterable-type="substring">
+
+
+ <div class="select-menu-item js-navigation-item selected">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/jiixyj/libebur128/blob/master/COPYING"
+ data-name="master"
+ data-skip-pjax="true"
+ rel="nofollow"
+ class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target"
+ title="master">master</a>
+ </div> <!-- /.select-menu-item -->
+ </div>
+
+ <div class="select-menu-no-results">Nothing to show</div>
+ </div> <!-- /.select-menu-list -->
+
+ <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-tab-filter="tags">
+ <div data-filterable-for="context-commitish-filter-field" data-filterable-type="substring">
+
+
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/jiixyj/libebur128/tree/v1.0.1/COPYING"
+ data-name="v1.0.1"
+ data-skip-pjax="true"
+ rel="nofollow"
+ class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target"
+ title="v1.0.1">v1.0.1</a>
+ </div> <!-- /.select-menu-item -->
+ <div class="select-menu-item js-navigation-item ">
+ <span class="select-menu-item-icon octicon octicon-check"></span>
+ <a href="/jiixyj/libebur128/tree/v1.0.0/COPYING"
+ data-name="v1.0.0"
+ data-skip-pjax="true"
+ rel="nofollow"
+ class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target"
+ title="v1.0.0">v1.0.0</a>
+ </div> <!-- /.select-menu-item -->
+ </div>
+
+ <div class="select-menu-no-results">Nothing to show</div>
+ </div> <!-- /.select-menu-list -->
+
+ </div> <!-- /.select-menu-modal -->
+ </div> <!-- /.select-menu-modal-holder -->
+</div> <!-- /.select-menu -->
+
+ <div class="breadcrumb">
+ <span class='repo-root js-repo-root'><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/jiixyj/libebur128" data-branch="master" data-direction="back" data-pjax="true" itemscope="url"><span itemprop="title">libebur128</span></a></span></span><span class="separator"> / </span><strong class="final-path">COPYING</strong> <span class="js-zeroclipboard minibutton zeroclipboard-button" data-clipboard-text="COPYING" data-copied-hint="copied!" title="copy to clipboard"><span class="octicon octicon-clippy"></span></span>
+ </div>
+</div>
+
+
+
+ <div class="commit file-history-tease">
+ <img class="main-avatar" height="24" src="https://1.gravatar.com/avatar/3b5bd17d0af4e7a25c76e01905abeebe?d=https%3A%2F%2Fidenticons.github.com%2F6022381812a68b1ea9816ac0417536f7.png&r=x&s=140" width="24" />
+ <span class="author"><a href="/jiixyj" rel="author">jiixyj</a></span>
+ <time class="js-relative-date" datetime="2011-03-14T08:06:24-07:00" title="2011-03-14 08:06:24">March 14, 2011</time>
+ <div class="commit-title">
+ <a href="/jiixyj/libebur128/commit/8d5617cc4bac6a22dbf9d1ce4af8668fbb0a866d" class="message" data-pjax="true" title="rename LICENSE to COPYING">rename LICENSE to COPYING</a>
+ </div>
+
+ <div class="participation">
+ <p class="quickstat"><a href="#blob_contributors_box" rel="facebox"><strong>1</strong> contributor</a></p>
+
+ </div>
+ <div id="blob_contributors_box" style="display:none">
+ <h2 class="facebox-header">Users who have contributed to this file</h2>
+ <ul class="facebox-user-list">
+ <li class="facebox-user-list-item">
+ <img height="24" src="https://1.gravatar.com/avatar/3b5bd17d0af4e7a25c76e01905abeebe?d=https%3A%2F%2Fidenticons.github.com%2F6022381812a68b1ea9816ac0417536f7.png&r=x&s=140" width="24" />
+ <a href="/jiixyj">jiixyj</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+<div id="files" class="bubble">
+ <div class="file">
+ <div class="meta">
+ <div class="info">
+ <span class="icon"><b class="octicon octicon-file-text"></b></span>
+ <span class="mode" title="File Mode">file</span>
+ <span>20 lines (16 sloc)</span>
+ <span>1.059 kb</span>
+ </div>
+ <div class="actions">
+ <div class="button-group">
+ <a class="minibutton tooltipped upwards"
+ title="Clicking this button will automatically fork this project so you can edit the file"
+ href="/jiixyj/libebur128/edit/master/COPYING"
+ data-method="post" rel="nofollow">Edit</a>
+ <a href="/jiixyj/libebur128/raw/master/COPYING" class="button minibutton " id="raw-url">Raw</a>
+ <a href="/jiixyj/libebur128/blame/master/COPYING" class="button minibutton ">Blame</a>
+ <a href="/jiixyj/libebur128/commits/master/COPYING" class="button minibutton " rel="nofollow">History</a>
+ </div><!-- /.button-group -->
+ <a class="minibutton danger empty-icon tooltipped downwards"
+ href="/jiixyj/libebur128/delete/master/COPYING"
+ title="Fork this project and delete file"
+ data-method="post" data-test-id="delete-blob-file" rel="nofollow">
+ Delete
+ </a>
+ </div><!-- /.actions -->
+
+ </div>
+ <div class="blob-wrapper data type-text js-blob-data">
+ <table class="file-code file-diff">
+ <tr class="file-code-line">
+ <td class="blob-line-nums">
+ <span id="L1" rel="#L1">1</span>
+<span id="L2" rel="#L2">2</span>
+<span id="L3" rel="#L3">3</span>
+<span id="L4" rel="#L4">4</span>
+<span id="L5" rel="#L5">5</span>
+<span id="L6" rel="#L6">6</span>
+<span id="L7" rel="#L7">7</span>
+<span id="L8" rel="#L8">8</span>
+<span id="L9" rel="#L9">9</span>
+<span id="L10" rel="#L10">10</span>
+<span id="L11" rel="#L11">11</span>
+<span id="L12" rel="#L12">12</span>
+<span id="L13" rel="#L13">13</span>
+<span id="L14" rel="#L14">14</span>
+<span id="L15" rel="#L15">15</span>
+<span id="L16" rel="#L16">16</span>
+<span id="L17" rel="#L17">17</span>
+<span id="L18" rel="#L18">18</span>
+<span id="L19" rel="#L19">19</span>
+
+ </td>
+ <td class="blob-line-code">
+ <div class="code-body highlight"><pre><div class='line' id='LC1'>Copyright (c) 2011 Jan Kokemüller</div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'>Permission is hereby granted, free of charge, to any person obtaining a copy</div><div class='line' id='LC4'>of this software and associated documentation files (the "Software"), to deal</div><div class='line' id='LC5'>in the Software without restriction, including without limitation the rights</div><div class='line' id='LC6'>to use, copy, modify, merge, publish, distribute, sublicense, and/or sell</div><div class='line' id='LC7'>copies of the Software, and to permit persons to whom the Software is</div><div class='line' id='LC8'>furnished to do so, subject to the following conditions:</div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>The above copyright notice and this permission notice shall be included in</div><div class='line' id='LC11'>all copies or substantial portions of the Software.</div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR</div><div class='line' id='LC14'>IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,</div><div class='line' id='LC15'>FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE</div><div class='line' id='LC16'>AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER</div><div class='line' id='LC17'>LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,</div><div class='line' id='LC18'>OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN</div><div class='line' id='LC19'>THE SOFTWARE.</div></pre></div>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ </div>
+</div>
+
+<a href="#jump-to-line" rel="facebox[.linejump]" data-hotkey="l" class="js-jump-to-line" style="display:none">Jump to Line</a>
+<div id="jump-to-line" style="display:none">
+ <form accept-charset="UTF-8" class="js-jump-to-line-form">
+ <input class="linejump-input js-jump-to-line-field" type="text" placeholder="Jump to line…" autofocus>
+ <button type="submit" class="button">Go</button>
+ </form>
+</div>
+
+ </div>
+
+ </div><!-- /.repo-container -->
+ <div class="modal-backdrop"></div>
+ </div><!-- /.container -->
+ </div><!-- /.site -->
+
+
+ </div><!-- /.wrapper -->
+
+ <div class="container">
+ <div class="site-footer">
+ <ul class="site-footer-links right">
+ <li><a href="https://status.github.com/">Status</a></li>
+ <li><a href="http://developer.github.com">API</a></li>
+ <li><a href="http://training.github.com">Training</a></li>
+ <li><a href="http://shop.github.com">Shop</a></li>
+ <li><a href="/blog">Blog</a></li>
+ <li><a href="/about">About</a></li>
+
+ </ul>
+
+ <a href="/">
+ <span class="mega-octicon octicon-mark-github" title="GitHub"></span>
+ </a>
+
+ <ul class="site-footer-links">
+ <li>© 2014 <span title="0.03197s from github-fe130-cp1-prd.iad.github.net">GitHub</span>, Inc.</li>
+ <li><a href="/site/terms">Terms</a></li>
+ <li><a href="/site/privacy">Privacy</a></li>
+ <li><a href="/security">Security</a></li>
+ <li><a href="/contact">Contact</a></li>
+ </ul>
+ </div><!-- /.site-footer -->
+</div><!-- /.container -->
+
+
+ <div class="fullscreen-overlay js-fullscreen-overlay" id="fullscreen_overlay">
+ <div class="fullscreen-container js-fullscreen-container">
+ <div class="textarea-wrap">
+ <textarea name="fullscreen-contents" id="fullscreen-contents" class="js-fullscreen-contents" placeholder="" data-suggester="fullscreen_suggester"></textarea>
+ <div class="suggester-container">
+ <div class="suggester fullscreen-suggester js-navigation-container" id="fullscreen_suggester"
+ data-url="/jiixyj/libebur128/suggestions/commit">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="fullscreen-sidebar">
+ <a href="#" class="exit-fullscreen js-exit-fullscreen tooltipped leftwards" title="Exit Zen Mode">
+ <span class="mega-octicon octicon-screen-normal"></span>
+ </a>
+ <a href="#" class="theme-switcher js-theme-switcher tooltipped leftwards"
+ title="Switch themes">
+ <span class="octicon octicon-color-mode"></span>
+ </a>
+ </div>
+</div>
+
+
+
+ <div id="ajax-error-message" class="flash flash-error">
+ <span class="octicon octicon-alert"></span>
+ <a href="#" class="octicon octicon-remove-close close ajax-error-dismiss"></a>
+ Something went wrong with that request. Please try again.
+ </div>
+
+ </body>
+</html>
+
--- /dev/null
+/* See COPYING file for copyright and license details. */
+
+#include "ebur128.h"
+
+#include <float.h>
+#include <limits.h>
+#include <math.h> /* You may have to define _USE_MATH_DEFINES if you use MSVC */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* This can be replaced by any BSD-like queue implementation. */
+#include <sys/queue.h>
+
+#ifdef USE_SPEEX_RESAMPLER
+ #include <speex/speex_resampler.h>
+#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 {
+ return NULL;
+ }
+ 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 <xmmintrin.h>
+#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 > 4) 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_LEFT_SURROUND ||
+ st->d->channel_map[c] == EBUR128_RIGHT_SURROUND) {
+ 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_gated_loudness(ebur128_state** sts, size_t size,
+ double* out) {
+ struct ebur128_dq_entry* it;
+ double relative_threshold = 0.0;
+ double gated_loudness = 0.0;
+ size_t above_thresh_counter = 0;
+ 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;
+ if (sts[i]->d->use_histogram) {
+ for (j = 0; j < 1000; ++j) {
+ relative_threshold += 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) {
+ ++above_thresh_counter;
+ relative_threshold += it->z;
+ }
+ }
+ }
+ if (!above_thresh_counter) {
+ *out = -HUGE_VAL;
+ return EBUR128_SUCCESS;
+ }
+ relative_threshold /= (double) above_thresh_counter;
+ relative_threshold *= relative_gate_factor;
+ 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_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
--- /dev/null
+/* 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 0
+#define EBUR128_VERSION_PATCH 1
+
+#include <stddef.h> /* for size_t */
+
+/** \enum channel
+ * Use these values when setting the channel map with ebur128_set_channel().
+ */
+enum channel {
+ EBUR128_UNUSED = 0, /**< unused channel (for example LFE channel) */
+ EBUR128_LEFT, /**< left channel */
+ EBUR128_RIGHT, /**< right channel */
+ EBUR128_CENTER, /**< center channel */
+ EBUR128_LEFT_SURROUND, /**< left surround channel */
+ EBUR128_RIGHT_SURROUND, /**< right surround channel */
+ EBUR128_DUAL_MONO /**< a channel that is counted twice */
+};
+
+/** \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_gated_loudness_* */
+ 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EBUR128_H_ */
extern mlt_filter filter_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
extern mlt_filter filter_charcoal_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
extern mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
extern mlt_filter filter_invert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
extern mlt_filter filter_sepia_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
extern mlt_producer producer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
MLT_REGISTER( filter_type, "charcoal", filter_charcoal_init );
MLT_REGISTER( filter_type, "dynamictext", filter_dynamictext_init );
MLT_REGISTER( filter_type, "invert", filter_invert_init );
+ MLT_REGISTER( filter_type, "loudness", filter_loudness_init );
MLT_REGISTER( filter_type, "sepia", filter_sepia_init );
MLT_REGISTER( producer_type, "blipflash", producer_blipflash_init );
MLT_REGISTER( producer_type, "count", producer_count_init );
MLT_REGISTER_METADATA( filter_type, "charcoal", metadata, "filter_charcoal.yml" );
MLT_REGISTER_METADATA( filter_type, "dynamictext", metadata, "filter_dynamictext.yml" );
MLT_REGISTER_METADATA( filter_type, "invert", metadata, "filter_invert.yml" );
+ MLT_REGISTER_METADATA( filter_type, "loudness", metadata, "filter_loudness.yml" );
MLT_REGISTER_METADATA( filter_type, "sepia", metadata, "filter_sepia.yml" );
MLT_REGISTER_METADATA( producer_type, "blipflash", metadata, "producer_blipflash.yml" );
MLT_REGISTER_METADATA( producer_type, "count", metadata, "producer_count.yml" );
--- /dev/null
+/*
+ * filter_ebur128.c -- normalize audio according to EBU R128
+ * Copyright (C) 2014 Brian Matherly <pez4brian@yahoo.com>
+ * Author: Brian Matherly <pez4brian@yahoo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <framework/mlt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "ebur128/ebur128.h"
+
+#define MAX_RESULT_SIZE 512
+
+typedef struct
+{
+ ebur128_state* state;
+} analyze_data;
+
+typedef struct
+{
+ double in_loudness;
+ double in_range;
+ double in_peak;
+ double coeff;
+} apply_data;
+
+typedef struct
+{
+ analyze_data* analyze;
+ apply_data* apply;
+ mlt_position last_position;
+} private_data;
+
+static void destroy_analyze_data( mlt_filter filter )
+{
+ private_data* private = (private_data*)filter->child;
+ ebur128_destroy( &private->analyze->state );
+ free( private->analyze );
+ private->analyze = NULL;
+}
+
+static void init_analyze_data( mlt_filter filter, int channels, int samplerate )
+{
+ private_data* private = (private_data*)filter->child;
+ private->analyze = (analyze_data*)calloc( 1, sizeof(analyze_data) );
+ private->analyze->state = ebur128_init( (unsigned int)channels, (unsigned long)samplerate, EBUR128_MODE_I | EBUR128_MODE_LRA | EBUR128_MODE_SAMPLE_PEAK );
+ private->last_position = 0;
+}
+
+static void destroy_apply_data( mlt_filter filter )
+{
+ private_data* private = (private_data*)filter->child;
+ free( private->apply );
+ private->apply = NULL;
+}
+
+static void init_apply_data( mlt_filter filter )
+{
+ private_data* private = (private_data*)filter->child;
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+ char* results = mlt_properties_get( properties, "results" );
+ int scan_return = 0;
+
+ private->apply = (apply_data*)calloc( 1, sizeof(apply_data) );
+
+ scan_return = sscanf( results,"L: %lf\tR: %lf\tP %lf\n", &private->apply->in_loudness, &private->apply->in_range, &private->apply->in_peak );
+ if( scan_return != 3 )
+ {
+ mlt_log_error( MLT_FILTER_SERVICE( filter ), "Unable to load results: %s\n", results );
+ destroy_apply_data( filter );
+ return;
+ }
+ else
+ {
+ double target_db = mlt_properties_get_double( properties, "program" );
+ double delta_db = target_db - private->apply->in_loudness;
+ private->apply->coeff = delta_db > -90.0f ? powf(10.0f, delta_db * 0.05f) : 0.0f;
+ mlt_log_info( MLT_FILTER_SERVICE( filter ), "Loaded Results: L: %lf\tR: %lf\tP %lf\n", private->apply->in_loudness, private->apply->in_range, private->apply->in_peak );
+ mlt_log_info( MLT_FILTER_SERVICE( filter ), "Coefficient: %lf\n", private->apply->coeff );
+ }
+}
+
+static void analyze( mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ private_data* private = (private_data*)filter->child;
+ mlt_position pos = mlt_filter_get_position( filter, frame );
+
+ // If any frames are skipped, analysis data will be incomplete.
+ if( private->analyze && pos != private->last_position + 1 )
+ {
+ mlt_log_error( MLT_FILTER_SERVICE(filter), "Analysis Failed: Bad frame sequence\n" );
+ destroy_analyze_data( filter );
+ }
+
+ // Analyze Audio
+ if( !private->analyze && pos == 0 )
+ {
+ init_analyze_data( filter, *channels, *frequency );
+ }
+
+ if( private->analyze )
+ {
+ ebur128_add_frames_float( private->analyze->state, *buffer, *samples );
+
+ if ( pos + 1 == mlt_filter_get_length2( filter, frame ) )
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+ double loudness = 0.0;
+ double range = 0.0;
+ double tmpPeak = 0.0;
+ double peak = 0.0;
+ int i = 0;
+ char result[MAX_RESULT_SIZE];
+ ebur128_loudness_global( private->analyze->state, &loudness );
+ ebur128_loudness_range( private->analyze->state, &range );
+
+ for ( i = 0; i < *channels; i++ )
+ {
+ ebur128_sample_peak( private->analyze->state, i, &tmpPeak );
+ if( tmpPeak > peak )
+ {
+ peak = tmpPeak;
+ }
+ }
+
+ snprintf( result, MAX_RESULT_SIZE, "L: %lf\tR: %lf\tP %lf\n", loudness, range, peak );
+ result[ MAX_RESULT_SIZE - 1 ] = '\0';
+ mlt_log_info( MLT_FILTER_SERVICE( filter ), "Stored results: %s", result );
+ mlt_properties_set( properties, "results", result );
+ destroy_analyze_data( filter );
+ }
+
+ private->last_position = pos;
+ }
+}
+
+static void apply( mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ private_data* private = (private_data*)filter->child;
+
+ // Analyze Audio
+ if( !private->apply )
+ {
+ init_apply_data( filter );
+ }
+
+ if( private->apply )
+ {
+ float* p = *buffer;
+ int count = *samples * *channels;
+ while( count-- )
+ {
+ *p = *p * private->apply->coeff;
+ p++;
+ }
+ }
+}
+
+/** Get the audio.
+*/
+
+static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
+{
+ mlt_filter filter = mlt_frame_pop_audio( frame );
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+
+ mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
+
+ // Get the producer's audio
+ *format = mlt_audio_f32le;
+ mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
+
+ char* results = mlt_properties_get( properties, "results" );
+ if( results && strcmp( results, "" ) )
+ {
+ apply( filter, frame, buffer, format, frequency, channels, samples );
+ }
+ else
+ {
+ analyze( filter, frame, buffer, format, frequency, channels, samples );
+ }
+
+ mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
+
+ return 0;
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
+{
+ mlt_frame_push_audio( frame, filter );
+ mlt_frame_push_audio( frame, filter_get_audio );
+ return frame;
+}
+
+/** Destructor for the filter.
+*/
+
+static void filter_close( mlt_filter filter )
+{
+ private_data* private = (private_data*)filter->child;
+
+ if ( private )
+ {
+ if ( private->analyze )
+ {
+ destroy_analyze_data( filter );
+ }
+ free( private );
+ }
+ filter->child = NULL;
+ filter->close = NULL;
+ filter->parent.close = NULL;
+ mlt_service_close( &filter->parent );
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+ mlt_filter filter = mlt_filter_new( );
+ private_data* data = (private_data*)calloc( 1, sizeof(private_data) );
+
+ if ( filter && data )
+ {
+ mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+ mlt_properties_set( properties, "program", "-23.0" );
+
+ data->analyze = NULL;
+
+ filter->close = filter_close;
+ filter->process = filter_process;
+ filter->child = data;
+ }
+ else
+ {
+ if( filter )
+ {
+ mlt_filter_close( filter );
+ filter = NULL;
+ }
+
+ if( data )
+ {
+ free( data );
+ }
+ }
+
+ return filter;
+}
--- /dev/null
+schema_version: 0.1
+type: filter
+identifier: loudness
+title: Loudness
+version: 1
+copyright: Brian Matherly <pez4brian@yahoo.com>
+creator: Brian Matherly <pez4brian@yahoo.com>
+license: LGPLv2.1
+language: en
+tags:
+ - Audio
+description: Correct audio loudness as recommended by EBU R128.
+notes: >
+ This filter requires two passes. The first pass performs analysis and stores
+ the result in the "results" property. The second pass applies the results to
+ the audio in order to achieve the desired loudness over the range of the
+ filter.
+
+parameters:
+ - identifier: results
+ title: Analysis Results
+ type: string
+ description: >
+ Set after analysis. Used during application.
+ Loudness information about the original audio.
+ When results are not supplied, the filter computes the results and stores
+ them in this property when the last frame has been processed.
+ mutable: no
+
+ - identifier: program
+ title: Target Program Loudness
+ type: float
+ description: >
+ Used during application.
+ The target program loudness in LUFS (Loudness Units Full Scale).
+ readonly: no
+ mutable: no
+ default: -23.0
+ minimum: -50.0
+ maximum: -10.0
+ unit: LUFS