libebur128 - (yet another) EBU R 128 implementation |
libebur128 - (yet another) EBU R 128 implementation |
Jan 12 2011, 17:08
Post
#1
|
|
![]() Group: Developer Posts: 224 Joined: 14-September 04 Member No.: 17002 |
Hi,
inspired by the wonderful work in this thread, I wrote my own implementation of the EBU R 128 standard. It is written in plain ANSI C and designed as a library, so you can use it in your own code. It's licensed under the MIT license. I've also implemented a simple scanning tool, which outputs something like this: CODE $ ./r128-sndfile -l -p both ~/music/bad\ loop\ -\ Luo/*.flac -12.81 LUFS, LRA: 14.16 LU, sample peak: 0.89151001, true peak: 0.99826229, /home/jan/music/bad loop - Luo/bad loop - Luo - 01 Nio.flac -11.15 LUFS, LRA: 8.26 LU, sample peak: 0.89163208, true peak: 0.99095666, /home/jan/music/bad loop - Luo/bad loop - Luo - 02 Eri Valeire.flac -10.14 LUFS, LRA: 11.79 LU, sample peak: 0.89154053, true peak: 0.99171823, /home/jan/music/bad loop - Luo/bad loop - Luo - 03 Kauniit Ihmiset.flac -11.31 LUFS, LRA: 11.75 LU, sample peak: 0.89157104, true peak: 0.92898595, /home/jan/music/bad loop - Luo/bad loop - Luo - 04 Mmin.flac -26.13 LUFS, LRA: 14.87 LU, sample peak: 0.25204468, true peak: 0.25203928, /home/jan/music/bad loop - Luo/bad loop - Luo - 05 3b Or T.flac -14.10 LUFS, LRA: 11.40 LU, sample peak: 0.89151001, true peak: 1.02603507, /home/jan/music/bad loop - Luo/bad loop - Luo - 06 Kannas Nsp.flac -------------------------------------------------------------------------------- -11.75 LUFS, LRA: 13.34 LU, sample peak: 0.89163208, true peak: 1.02603507 There is also ReplayGain tagging, using a reference level of -18 LUFS to match RG's loudness: CODE r128-mpg123 -t album [FILE|DIRECTORY]... r128-mpg123 -t track [FILE|DIRECTORY]... Download current version here. This post has been edited by Raiden: Feb 27 2011, 22:29 |
|
|
|
![]() |
Jan 17 2011, 19:11
Post
#2
|
|
|
Winamp Developer Group: Developer Posts: 662 Joined: 17-July 05 From: Ashburn, VA Member No.: 23375 |
Correct.
V are gain values and have no relation to sampling frequency Q is a "magic number" that effects the shape of the filter. Fc stays constant - it's the nominal cutoff frequency. ω is tan(fc/fs *π) [it has been typo'd as Ω in the paper, ω/Ω are lowercase/uppercase pairs]. That is, it's the cutoff frequency as a percentage of the sampling rate, and "pre-warped" with tan() to match the frequency warping done by the bilinear transform. k is often used for ω in source code. |
|
|
|
Jan 19 2011, 16:40
Post
#3
|
|
![]() Group: Members Posts: 395 Joined: 13-June 10 Member No.: 81467 |
Correct. V are gain values and have no relation to sampling frequency Q is a "magic number" that effects the shape of the filter. Fc stays constant - it's the nominal cutoff frequency. ω is tan(fc/fs *π) [it has been typo'd as Ω in the paper, ω/Ω are lowercase/uppercase pairs]. That is, it's the cutoff frequency as a percentage of the sampling rate, and "pre-warped" with tan() to match the frequency warping done by the bilinear transform. k is often used for ω in source code. I've finally managed (thanks to the pointers provided by Raiden) to find a closed solution to the re-quantization problem for digital biquad filters as it appears in the context of (but not restricted to) BS.1770. Assume youv'e given the coefficients a1, a2, b0, b1, b2, b3 of a digital biquad filter for a particular sample frequency Fs (cf. e.g. http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt). The re-quantization problem consists in calculating the coefficents a1', a2', b0', b1', b2', and b3' of a digital biquad filter with the same characteristics as the given one, but for another sample frequency Fs'. The key to the solution are the pointers provided by Raiden:
CODE (1) (1 + K / Q + K^2) * a1 = 2 * (K^2 - 1) (2) (1 + K / Q + K^2) * a2 = 1 - K / Q + K^2 (3) (1 + K / Q + K^2) * b0 = Vh + Vb * K / Q + Vl * K^2 (4) (1 + K / Q + K^2) * b1 = 2 * (Vl * K^2 - Vh) (5) (1 + K / Q + K^2) * b2 = Vh - Vb * K / Q + Vl * K^2 with CODE (6) K = tan(pi * Fc / Fs). In order to solve the above stated re-quantization problem we do the following:
CODE x11 = a1 - 2 x12 = a1 x1 = -a1 - 2 x21 = a2 - 1 x22 = a2 + 1 x2 = -a2 + 1 DX = x22 * x11 - x12 * x21 and using well known methods we arrive at CODE (6) K^2 = (x22 * x1 - x12 * x2) / DX (7) K/Q = (x11 * x2 - x21 * x1) / DX Next we solve eqs. (3), (4), and (5) for Vh, Vb, and Vl. Introducing CODE (8) a0 = 1 + K / Q + K^2 and reordering eqs. (3), (4), and (5) they read CODE (3a) Vh + K/Q * Vb + K^2 * Vl = b0 * a0 (4a) -2 * Vh + (2 * K^2) * Vl = b1 * a0 (5a) Vh - K/Q * Vb + K^2 * Vl = b2 * a0 Now it's not hard any longer to find the solutions: CODE a0 * (b0 - b2) (9) Vb = -------------- 2 * K/Q a0 * (b0 + b1 + b2) (10) Vl = ------------------- 4 * K^2 a0 * (b0 - b1 + b2) (11) Vh = ------------------- 4 Finally we observe from eqs. (6) and (6') the following: CODE (6) K = tan(pi * Fc / Fs) (6)' K' = tan(pi * Fc / Fs') (12) K' = tan(atan(K) * Fs / Fs') Eqs. (6) and (7) along with (9), (10), (11), and (12) provide everything we need for re-quantizing any given digital biquad filter. We demonstrate this by the following C code (please note that this code re-quantizes digital biqad filters on the fly, no pre-processing by an external algebra system ist needed). CODE typedef struct biquad { double fs; double a1, a2; double b0, b1, b2; } biquad_t; typedef struct biquad_ps { double k; double q; double vb; double vl; double vh; } biquad_ps_t; void biquad_get_ps(biquad_t *biquad, biquad_ps_t *ps) { double x11 = biquad->a1 - 2; double x12 = biquad->a1; double x1 = -biquad->a1 - 2; double x21 = biquad->a2 - 1; double x22 = biquad->a2 + 1; double x2 = -biquad->a2 + 1; double dx = x22*x11 - x12*x21; double k_sq = (x22*x1 - x12*x2)/dx; double k_by_q = (x11*x2 - x21*x1)/dx; double a0 = 1.0 + k_by_q + k_sq; ps->k = sqrt(k_sq); ps->q = ps->k/k_by_q; ps->vb = 0.5*a0*(biquad->b0 - biquad->b2)/k_by_q; ps->vl = 0.25*a0*(biquad->b0 + biquad->b1 + biquad->b2)/k_sq; ps->vh = 0.25*a0*(biquad->b0 - biquad->b1 + biquad->b2); } biquad_t *biquad_requantize(biquad_t *in, biquad_t *out) { if (in->fs==out->fs) return in; else { biquad_ps_t ps; double k, k_sq, k_by_q, a0; biquad_get_ps(in, &ps); k=tan((in->fs/out->fs)*atan(ps.k)); k_sq = k*k; k_by_q = k/ps.q; a0 = 1.0 + k_by_q + k_sq; out->a1 = (2.0*(k_sq - 1.0))/a0; out->a2 = (1.0 - k_by_q + k_sq)/a0; out->b0 = (ps.vh + ps.vb*k_by_q + ps.vl*k_sq)/a0; out->b1 = (2.0 * (ps.vl*k_sq - ps.vh))/a0; out->b2 = (ps.vh - ps.vb*k_by_q + ps.vl*k_sq)/a0; return out; } } The following code demonstrates how to re-quantize the 48 kHz BS.1770 pre-filter to it's 44.1 kHz equivalent using the above functions. CODE int main(int argc, char **argv)
{ int i; biquad_t pre48000={ .fs=48000, .a1=-1.69065929318241, .a2=0.73248077421585, .b0=1.53512485958697, .b1=-2.69169618940638, .b2=1.19839281085285 }; biquad_t pre44100={ .fs=44100 }; biquad_requantize(&pre48000, &pre44100); printf("a1: %f, %f\n", pre48000.a1, pre44100.a1); printf("a2: %f, %f\n", pre48000.a2, pre44100.a2); printf("b1: %f, %f\n", pre48000.b0, pre44100.b0); printf("b2: %f, %f\n", pre48000.b1, pre44100.b1); printf("b3: %f, %f\n", pre48000.b2, pre44100.b2); } This post has been edited by pbelkner: Jan 19 2011, 16:41 |
|
|
|
Raiden libebur128 - (yet another) EBU R 128 implementation Jan 12 2011, 17:08
googlebot While I'm really a big proponent of modern cod... Jan 12 2011, 18:01
googlebot IMHO, you should both think about contributing you... Jan 12 2011, 18:58
pbelkner First of all congratulations, Raiden, for the grea... Jan 13 2011, 09:38
googlebot QUOTE (pbelkner @ Jan 13 2011, 09:38) On ... Jan 13 2011, 11:32
mudlord Nice nice work.
QUOTE IMHO, you should both think... Jan 12 2011, 20:14
pbelkner QUOTE (mudlord @ Jan 12 2011, 21:14) Why,... Jan 13 2011, 09:45
Raiden QUOTE (googlebot @ Jan 12 2011, 18:01) Th... Jan 12 2011, 20:14
googlebot Great! Even gets the 6-channel case right in c... Jan 12 2011, 20:28
mudlord Hmmm, shouldn't ebur128_write_frames be ebur... Jan 12 2011, 21:49
Raiden QUOTE (googlebot @ Jan 12 2011, 20:28) Gr... Jan 12 2011, 21:52
pbelkner QUOTE (Raiden @ Jan 12 2011, 22:52) QUOTE... Jan 13 2011, 09:51
Raiden QUOTE (mudlord @ Jan 12 2011, 21:49) Hmmm... Jan 12 2011, 22:12
googlebot QUOTE (Raiden @ Jan 12 2011, 22:12) How d... Jan 12 2011, 23:07
benski Is there a good mapping between LUFS and ReplayGai... Jan 12 2011, 22:17
Raiden I've just uploaded version 0.1.2:
- fixed a ra... Jan 14 2011, 10:31
Raiden QUOTE (pbelkner @ Jan 13 2011, 09:51) Up ... Jan 14 2011, 10:40
pbelkner QUOTE (Raiden @ Jan 14 2011, 11:40) QUOTE... Jan 14 2011, 10:46
Raiden QUOTE (pbelkner @ Jan 14 2011, 10:46) Tha... Jan 14 2011, 11:01
Raiden 0.1.3 is up!
- Added tagging support. You need... Jan 16 2011, 00:51
C.R.Helmrich I gave this a try yesterday (r128-sndfile, version... Jan 16 2011, 14:28
pbelkner QUOTE (C.R.Helmrich @ Jan 16 2011, 15:28)... Jan 16 2011, 15:07
C.R.Helmrich QUOTE (C.R.Helmrich @ Jan 16 2011, 15:28)... Jan 16 2011, 16:13
pbelkner QUOTE (C.R.Helmrich @ Jan 16 2011, 17:13)... Jan 16 2011, 16:46

benski QUOTE (pbelkner @ Jan 16 2011, 10:46) On ... Jan 16 2011, 20:24

pbelkner QUOTE (benski @ Jan 16 2011, 21:24) QUOTE... Jan 16 2011, 20:32
Notat QUOTE (C.R.Helmrich @ Jan 16 2011, 15:28)... Jan 16 2011, 21:40
Raiden QUOTE (C.R.Helmrich @ Jan 16 2011, 14:28)... Jan 16 2011, 17:47
pbelkner QUOTE (Raiden @ Jan 16 2011, 18:47) I... Jan 16 2011, 18:49
Notat QUOTE (Raiden @ Jan 16 2011, 09:47) Yeste... Jan 16 2011, 21:46
pbelkner QUOTE (Raiden @ Jan 16 2011, 18:47) Yeste... Jan 17 2011, 18:22
lvqcl QUOTE now it only calls out coefficients for 48 an... Jan 16 2011, 21:58
Notat QUOTE (lvqcl @ Jan 16 2011, 13:58) QUOTE ... Jan 17 2011, 00:35
Raiden QUOTE (Notat @ Jan 16 2011, 21:46) QUOTE ... Jan 17 2011, 00:18
Raiden QUOTE (C.R.Helmrich @ Jan 16 2011, 14:28)... Jan 17 2011, 00:25
Raiden 0.1.4 has been uploaded, with the following new fe... Jan 17 2011, 00:42
verdemar Thanks, works very well for me.
Just thought I... Jan 17 2011, 21:36
Raiden QUOTE (verdemar @ Jan 17 2011, 21:36) Sub... Jan 18 2011, 23:53
Raiden Version 0.1.5 has arrived!
- I've seperate... Jan 19 2011, 00:21
Raiden QUOTE (pbelkner @ Jan 19 2011, 16:40) The... Jan 23 2011, 16:49
Raiden I've uploaded 0.1.6:
- The main new feature is... Jan 23 2011, 17:00
hödyr Any idea why r128-mpg123 does not work for me on W... Jan 23 2011, 18:17
C.R.Helmrich Thanks for the new version. When I check the same ... Jan 23 2011, 20:00
Raiden QUOTE (C.R.Helmrich @ Jan 23 2011, 20:00)... Jan 23 2011, 20:28
Raiden QUOTE (hödyr @ Jan 23 2011, 18:17) Any id... Jan 23 2011, 20:42
Raiden Version 0.1.7 is just a bug fix:
- Handle MP3... Jan 23 2011, 23:05
hödyr Thanks. Could you fix the build script to run on F... Jan 24 2011, 11:55
Raiden QUOTE (hödyr @ Jan 24 2011, 11:55) Thanks... Jan 24 2011, 12:45
Raiden I've uploaded 0.1.8:
- Some improvements to th... Jan 24 2011, 23:50
kode54 I've made my own preliminary foo_r128scan comp... Jan 26 2011, 06:25
Raiden I've uploaded 0.1.9. This is mostly a library ... Jan 28 2011, 12:14
kode54 You made a mistake in ebur128_change_parameters:
... Jan 29 2011, 02:58
C.R.Helmrich And in ebur128.h it says:
QUOTE /* Get short-term... Jan 29 2011, 17:13
Raiden Thank you for the bug reports!
I've upload... Jan 30 2011, 21:04
gjgriffith Is SSE2 required to run this tool? The only versio... Jan 31 2011, 02:33
Raiden Here are non SSE builds:
http://www-public.tu-bs.d... Jan 31 2011, 09:51
gjgriffith QUOTE (Raiden @ Jan 31 2011, 09:51) Here ... Jan 31 2011, 19:34
nucelar QUOTE (gjgriffith @ Jan 31 2011, 19:34) Q... Feb 3 2011, 11:47
googlebot The R128 spec defines a measurement of short-term ... Feb 3 2011, 12:31
nucelar QUOTE (googlebot @ Feb 3 2011, 12:31) The... Feb 3 2011, 12:48
Raiden QUOTE (nucelar @ Feb 3 2011, 11:47) First... Feb 3 2011, 20:55
nucelar QUOTE (Raiden @ Feb 3 2011, 20:55) It is ... Feb 4 2011, 12:03
Raiden QUOTE (nucelar @ Feb 4 2011, 12:03) Later... Feb 5 2011, 18:42
Raiden I've uploaded 0.1.11. There are no changes to ... Feb 5 2011, 18:51
kode54 You may want to take a look at the silly changes I... Feb 6 2011, 08:50
googlebot QUOTE (kode54 @ Feb 6 2011, 08:50) You ma... Feb 6 2011, 09:15
Raiden QUOTE (kode54 @ Feb 6 2011, 08:50) You ma... Feb 6 2011, 11:42
googlebot QUOTE (Raiden @ Feb 6 2011, 11:42) I supp... Feb 6 2011, 11:46
pbelkner QUOTE (Raiden @ Feb 6 2011, 11:42) Tech 3... Feb 6 2011, 11:59
leirbag Hi,
I have just tested last version (libebur128-0... Feb 6 2011, 12:28
Raiden Thanks for the report! It was a bug in the com... Feb 6 2011, 13:38
leirbag QUOTE (Raiden @ Feb 6 2011, 13:38) Thanks... Feb 6 2011, 14:14
leirbag QUOTE (Raiden @ Feb 6 2011, 13:38) Thanks... Feb 6 2011, 14:19
benski Raiden.
For gated loudness of a segment, the EBU ... Feb 6 2011, 20:51
Raiden QUOTE (benski @ Feb 6 2011, 20:51) For ga... Feb 6 2011, 22:16
benski And in the example of a 600ms input, it should be ... Feb 6 2011, 22:36
kode54 Ah, right, then. Well, then it would be helpful to... Feb 7 2011, 00:44
Raiden QUOTE (googlebot @ Feb 6 2011, 11:46) PS ... Feb 7 2011, 02:06
romor QUOTE (Raiden @ Feb 7 2011, 02:06) It cou... Feb 7 2011, 03:08
Raiden QUOTE (benski @ Feb 6 2011, 22:36) And in... Feb 7 2011, 02:21
benski QUOTE (Raiden @ Feb 6 2011, 20:21) QUOTE ... Feb 7 2011, 05:22
Raiden QUOTE (kode54 @ Feb 7 2011, 00:44) Ah, ri... Feb 7 2011, 02:40
Raiden It is doing window overlap. Otherwise, the block e... Feb 7 2011, 06:36
benski QUOTE (Raiden @ Feb 7 2011, 00:36) It is ... Feb 7 2011, 16:20
kode54 The library doesn't like it very much if the s... Feb 7 2011, 15:06
pbelkner QUOTE (kode54 @ Feb 7 2011, 15:06) /5*2.
... Feb 7 2011, 15:20
nucelar Hi raiden, you're last improvements/ additions... Feb 7 2011, 16:59
Raiden Hi again,
I've uploaded 0.2.0:
Library:
- Rem... Feb 20 2011, 18:55
habasud QUOTE (Raiden @ Feb 20 2011, 18:55) Hi ag... Feb 23 2011, 09:46
Raiden QUOTE (habasud @ Feb 23 2011, 09:46) It t... Feb 23 2011, 12:38
C.R.Helmrich FYI: EBU tech doc 3343 has been published.
Chris Feb 26 2011, 13:10
Raiden I've uploaded 0.2.1.
Library:
- removed the e... Feb 26 2011, 20:20
Raiden And here is 0.2.2, with a single change:
- added ... Feb 27 2011, 22:40
nucelar QUOTE (Raiden @ Feb 27 2011, 22:40) And h... Mar 3 2011, 18:01
Raiden QUOTE (nucelar @ Mar 3 2011, 18:01) Hey R... Mar 3 2011, 22:33
nucelar Hi!
Thanks for the Macports suggestion, i will... Mar 4 2011, 11:53
habasud ...maybe someone finds it useful to visualize the ... Mar 4 2011, 12:36
Surfi ::
SSE2 Win32 compile doesn't work with comma... Mar 5 2011, 18:37![]() ![]() |
|
Lo-Fi Version | Time is now: 24th May 2013 - 08:34 |