Closed mhartzel closed 10 years ago
Hi Mikael, libebur128 has a default channels allocation like this : 1 channel : double mono else : L/R/C/LFE/Ls/Rs (which is the ITU/SMPTE standard order).
There's a function int ebur128_set_channel(ebur128_state* st, unsigned int channel_number, int value) that allows for customization if the application that uses libebur128 implements some channel reordering.
Now, you are raising some questions : 3 channels L/R/C should work with the default allocation 4 channels : could be a L/R/C/Surround configuration ... Not so much used today (It's the matrixed Dolby Surround system with a mono surround channel). How should it be measured ? BS 1770 is not really defined for this case, it depends if the mono surround channel is to be reproduced on one or two surround loudspeakers ... 5 channels : could be L/R/C/Ls/Rs. Simple default correction possible (quick&dirty, not tested yet) : (to insert after line 168 in ebur128.c, in the body of the ebur128_init_channel_map function)
if (st->channels == 5) { st->d->channel_map[3] = EBUR128_LEFT_SURROUND; st->d->channel_map[4] = EBUR128_RIGHT_SURROUND; }
Regards, Manuel
Hi Manuel :)
Thanks for the reply and sorry for the delayed answer. I was on a short trip abroad :)
Your suggestion seems to do the trick :) I had no time to do conclusive tests yet (will do later), just quickly patched ebur128.c according to your advice and ran a 'loudness scan -l *.wav on the EBU test files (http://tech.ebu.ch/webdav/site/tech/shared/testmaterial/ebu-loudness-test-setv03.zip)
Now 5.0 and 5.1 files both give the correct result of - 23.0 LUFS:
-23.0 LUFS, 0.0 LU, seq-3341-6-5channels-16bit.wav -23.0 LUFS, 0.0 LU, seq-3341-6-6channels-WAVEEX-16bit.wav
If I want to add 4.0 channel (L, R, LS, RS) and 5.0 (L, R, C, LS, RS) channel orders to your code then I assume the patch would be this (added after line 168 in ebur128.c):
if (st->channels == 4) { st->d->channel_map[2] = EBUR128_LEFT_SURROUND; st->d->channel_map[3] = EBUR128_RIGHT_SURROUND; }
if (st->channels == 5) { st->d->channel_map[3] = EBUR128_LEFT_SURROUND; st->d->channel_map[4] = EBUR128_RIGHT_SURROUND; }
If this works correctly then 'loudness' would support all the same channel orders as Flac does and I think this is all the users of my software need, meaning the following cases:
2.0 = L, R 3.0 = Both L, C, R and L, R, C supported 4.0 = L, R, LS, RS 5.0 = L, R, C, LS, RS 5.1 = L, R, C, LFE, LS, RS
Thanks very much for helping me out :)
The 4.0 case is an imaginary one, I do not know anybody using it in real life. However I think it could be used as L, R, LS, RS in some cases. We have some 4.0 field recorders that we use to record ambient sounds (wind in the forest, traffic driving by, etc). These L, R, LS, RS recordings are not meant to be used only by themselves, but used in a 5.0 or 5.1 mix as the surround atmosphere. This 4.0 is quite a nice format and works very well in these situations so I think somebody will probably someday broadcast something in this audio format :)
I have heard that Swedish television uses 3.0 format in a creative way. When people get older they often have trouble hearing speech if there is other sounds for example background music played at the same time. Swedish television uses 3.0 to help these people. They broadcast only speech in the Center channel and background sounds only in Left and Right. People can then use their home theater system to adjust speech up or background sound down :)
Mikael Hartzell
I just ran the patched version of 'loudness' across my 134 test files (mono, stereo and 5.1) and compared the results to the unpatched version. I can confirm that the patch does not break these channel counts. Now I need to make some more tests with 4.0 and 5.0 to confirm everything is okay before going ahead and releasing a new FreeLCS - version :)
Mikael Hartzell
Hi again :)
Today I tested the patch with channel counts 1 - 6 and also ran the same test files through two commercial Pro Tools EBU R128 meter plugins. The test confirms that patch works alright, the only problem is that the channel map created in the patch gets overwritten if audio files have their own channel maps in the file header. Need to do some more digging :)
Test shows that all results are within tolerance (0.1 dB), and also that Nugen plugin miscalculates mono files :)
Channels-1.0.wav: -21.2 Channels-2.0.wav: -19.0 Channels-3.0.wav: -18.9 Channels-4.0.wav: -17.3 Channels-5.0.wav: -17.5 Channels-5.1.wav: -17.5
SineWave-Channels-1.wav: -26.1 SineWave-Channels-2.wav: -23.1 SineWave-Channels-3.wav: -21.3 SineWave-Channels-4.wav: -19.2 SineWave-Channels-5.wav: -18.4 SineWave-Channels-6.wav: -18.4
WhiteNoise-Channels-1.wav: -24.7 WhiteNoise-Channels-2.wav: -21.7 WhiteNoise-Channels-3.wav: -19.9 WhiteNoise-Channels-4.wav: -17.9 WhiteNoise-Channels-5.wav: -17.0 WhiteNoise-Channels-6.wav: -17.0
Channels-1.0.wav: -19.7 Channels-2.0.wav: -19.1 Channels-3.0.wav: -18.9 Channels-4.0.wav: -17.3 Channels-5.0.wav: -17.5 Channels-5.1.wav: -17.5
SineWave-Channels-1.wav: -26.1 SineWave-Channels-2.wav: -23.1 SineWave-Channels-3.wav: -21.3 SineWave-Channels-4.wav: -19.2 SineWave-Channels-5.wav: -18.4 SineWave-Channels-6.wav: -18.4
WhiteNoise-Channels-1.wav: -24.7 WhiteNoise-Channels-2.wav: -21.7 WhiteNoise-Channels-3.wav: -19.9 WhiteNoise-Channels-4.wav: -17.9 WhiteNoise-Channels-5.wav: -17.0 WhiteNoise-Channels-6.wav: -17.0
Channels-1.0.wav: -19.8 Channels-2.0.wav: -19.1 Channels-3.0.wav: -18.9 Channels-4.0.wav: -17.3 Channels-5.0.wav: -17.5 Channels-5.1.wav: -17.5
SineWave-Channels-1.wav: -26.0 SineWave-Channels-2.wav: -23.0 SineWave-Channels-3.wav: -21.2 SineWave-Channels-4.wav: -19.2 SineWave-Channels-5.wav: -18.4 SineWave-Channels-6.wav: -18.4
WhiteNoise-Channels-1.wav: -24.6 WhiteNoise-Channels-2.wav: -21.6 WhiteNoise-Channels-3.wav: -19.9 WhiteNoise-Channels-4.wav: -17.8 WhiteNoise-Channels-5.wav: -17.0 WhiteNoise-Channels-6.wav: -17.0
Channels-1.0.wav: -19.8 Channels-2.0.wav: -19.1 Channels-3.0.wav: -18.9 Channels-4.0.wav: -18.7 Channels-5.0.wav: -17.7 Channels-5.1.wav: -17.5
SineWave-Channels-1.wav: -26.0 SineWave-Channels-2.wav: -23.0 SineWave-Channels-3.wav: -21.2 SineWave-Channels-4.wav: -21.2 SineWave-Channels-5.wav: -19.6 SineWave-Channels-6.wav: -18.4
WhiteNoise-Channels-1.wav: -24.6 WhiteNoise-Channels-2.wav: -21.6 WhiteNoise-Channels-3.wav: -19.9 WhiteNoise-Channels-4.wav: -19.9 WhiteNoise-Channels-5.wav: -18.2 WhiteNoise-Channels-6.wav: -17.0
I could not find any multichannel test files from the internet with channel counts from 3 to 5 and with known loudness. So the test results in my previous post might be useful for somebody trying to resolve problems in channel mapping and loudness calculation. The sine wave and white noise test files I used can be recreated with the bash script below (requires sox).
Measure loudness from the test files with command: loudness scan -l *.flac
Note: do not change sox output from flac to wav because sox inserts a channel map to 4.0 channel wavs it creates. Flac files created with sox don't have channel maps so libebur128 uses it's own internal default channel map in loudness calculation.
Mikael Hartzell
Shell script starts below ----------------------------------------------------------------------------------------------------------------
sox -n -b 16 TestSignal.flac synth 20 sine 1000
sox TestSignal.flac SineWave-Channels-1.flac gain -23 sox TestSignal.flac TestSignal.flac SineWave-Channels-2.flac -M gain -23 sox TestSignal.flac TestSignal.flac TestSignal.flac SineWave-Channels-3.flac -M gain -23 sox TestSignal.flac TestSignal.flac TestSignal.flac TestSignal.flac SineWave-Channels-4.flac -M gain -23 sox TestSignal.flac TestSignal.flac TestSignal.flac TestSignal.flac TestSignal.flac SineWave-Channels-5.flac -M gain -23 sox TestSignal.flac TestSignal.flac TestSignal.flac TestSignal.flac TestSignal.flac TestSignal.flac SineWave-Channels-6.flac -M gain -23
rm -f TestSignal.flac
sox -n -b 16 0_dBFS-1.flac synth 20 whitenoise 1000 sox -n -b 16 0_dBFS-2.flac synth 20 whitenoise 1000 sox -n -b 16 0_dBFS-3.flac synth 20 whitenoise 1000 sox -n -b 16 0_dBFS-4.flac synth 20 whitenoise 1000 sox -n -b 16 0_dBFS-5.flac synth 20 whitenoise 1000 sox -n -b 16 0_dBFS-6.flac synth 20 whitenoise 1000
sox 0_dBFS-1.flac WhiteNoise-Channels-1.flac gain -23 sox 0_dBFS-1.flac 0_dBFS-2.flac WhiteNoise-Channels-2.flac -M gain -23 sox 0_dBFS-1.flac 0_dBFS-2.flac 0_dBFS-3.flac WhiteNoise-Channels-3.flac -M gain -23 sox 0_dBFS-1.flac 0_dBFS-2.flac 0_dBFS-3.flac 0_dBFS-4.flac WhiteNoise-Channels-4.flac -M gain -23 sox 0_dBFS-1.flac 0_dBFS-2.flac 0_dBFS-3.flac 0_dBFS-4.flac 0_dBFS-5.flac WhiteNoise-Channels-5.flac -M gain -23 sox 0_dBFS-1.flac 0_dBFS-2.flac 0_dBFS-3.flac 0_dBFS-4.flac 0_dBFS-5.flac 0_dBFS-6.flac WhiteNoise-Channels-6.flac -M gain -23
rm -f 0_dBFS-1.flac rm -f 0_dBFS-2.flac rm -f 0_dBFS-3.flac rm -f 0_dBFS-4.flac rm -f 0_dBFS-5.flac rm -f 0_dBFS-6.flac
Dang :) Github text parser destroyed the shell script :)
All titles in the shell script are comments and the lines they are on should begin with an "#" otherwise the script will crash.
Or just download the shellscript from here: https://dl.dropbox.com/u/2071830/00-create_test_files-all.sh
M. Hartzell
Hello again :)
Ok, I found a way to disable usage of channel maps embedded in files :)
Here is the patch that forces 4.0 channel order to L, R, LS, RS and 5.0 to L, R, C, LS, RS and disables usage of file embedded channel maps:
https://dl.dropbox.com/u/2071830/libebur128_scanner_4.0_and_5.0_channel_mapping_hack.diff
Copy the file to the root of the git repository and apply with:
git apply libebur128_scanner_4.0_and_5.0_channel_mapping_hack.diff
Ignore warnings about white space errors.
It's quite a hack and just a temporary solution to my specific problem. The long term solution would be to implement some kind of command line options to enable the user to specify rear and lfe channels. I already took a look at this, but since my C - skills are close to zero it's too hard for me :) Libebur128 uses GOptions (http://www.gtk.org/api/2.6/glib/glib-Commandline-option-parser.html) to parse commandline and syntax is not too hard to grasp. The trickiest part is to hack someone elses code without breaking anything :)
The easiest way to implement command line options would in my opinion be this:
0 = LFE / unused 1 = front channel 2 = surround channel
Initialize a indexed variable that has 6 items, one for each channel: user_defined_channel_map = 0, 0, 0, 0, 0, 0
User gives commandline: loudness scan -surround=5,6 -lfe=4 six_channel_audiofile.wav
Get number of channels and set all existing channels to front: user_defined_channel_map = 1, 1, 1, 1, 1, 1
Set user given channels to the channel map: user_defined_channel_map = 1, 1, 1, 0, 2, 2
Disable reading of channel map embedded in audio files (input_sndfile.c, input_ffmpeg.c, input_gstreamer.c)
If the input file is 4.0:
Initialize channel map: user_defined_channel_map = 0, 0, 0, 0, 0, 0
Commandline: loudness scan -surround=3,4 four_channel_audiofile.wav
Get number of channels and set all existing channels to front: user_defined_channel_map = 1, 1, 1, 1, 0, 0
Set user given channels to the channel map: user_defined_channel_map = 1, 1, 2, 2, 0, 0
Disable reading of channel map embedded in audio files
This solution could be easily merged with the default channel map initialization that is now in: ebur128.c
M. Hartzell
I ran the same initial test cases @mhartzel did and noticed the failures as well. That led me here. After reading over the thread, I think a decent solution is proposed, but being applied to the wrong location.
The job of labeling the channels for the core library falls to the scanner, who juggles the input libraries. The core library is blind in this regard, and is fed what we tell it. To me this problem of mislabeled channels seems like a scanner issue.
Below I have a patch that is more or less the same as what @mhartzel proposed. The advantage in this case is that the input libraries are still used to detect embedded channel maps. They have been doing their job fine this whole time. It is the files without these maps that are the problem, and in those cases we just make a few dumb assumptions that seem to keep things running smoothly. All the tests seem to run fine after this patch.
diff --git a/scanner/scanner-common/scanner-common.c b/scanner/scanner-common/scanner-common.c
index 6f9e010..7aa0213 100644
--- a/scanner/scanner-common/scanner-common.c
+++ b/scanner/scanner-common/scanner-common.c
@@ -139,6 +139,20 @@ void init_state_and_scan_work_item(struct filename_list_node *fln, struct scan_o
for (i = 0; i < fd->st->channels; ++i) {
ebur128_set_channel(fd->st, i, channel_map[i]);
}
+ } else {
+ /* Make assumptions when input libraries do not find channel maps */
+ switch (fd->st->channels) {
+ case (4):
+ ebur128_set_channel(fd->st, 2, EBUR128_LEFT_SURROUND);
+ ebur128_set_channel(fd->st, 3, EBUR128_RIGHT_SURROUND);
+ break;
+ case (5):
+ ebur128_set_channel(fd->st, 3, EBUR128_LEFT_SURROUND);
+ ebur128_set_channel(fd->st, 4, EBUR128_RIGHT_SURROUND);
+ break;
+ default:
+ break;
+ }
}
free(channel_map);
I have seen audio files that have incorrect channel mapping. Some programs might automatically create channels maps based on some assumptions. There are not so many programs around that let you define your channel map when you save your audio file. So to assume that channel maps embedded in files are always correct might lead to problems.
Because of this I think the best solution would be to let the user override possible channel maps in audio files. This would enable the user to decide if he wants to use the channel map embedded in the file or not.
The best way to implement this would be a commandline option.
I fixed this in the library now. Previously, for example, 5 channel files would get a default channel map of { left, right, center, unused, left_surround }, which was clearly broken :) Now, 4 and 5 channel files are special cased.
I agree that the scanner should grow an option to override default/detected channel maps. I'll open a bug report on the new loudness-scanner repo.
The new issue is here: https://github.com/jiixyj/loudness-scanner/issues/1
Hi :)
I've been actively beta testing my software (freelcs.sourceforge.net) and I may have found a small problem in libebur128 program scanner :)
EBU document 3341 ( download here: http://tech.ebu.ch/webdav/site/tech/shared/tech/tech3341.pdf ) has a couple of loudness calculation test cases on page 9. In test case 6 a 5.0 test file is created using sine waves. The loudness calculation result for this file should be -23.0 LUFS, but I get a result of -23.7 LUFS.
If add a silent channel to this 5.0 testfile as channel 4, then the loudness calculation gets the correct result of -23.0 LUFS.
How to recreate the problem
sox -n sine_-28dBFS.wav synth 20 sine 1000 gain -28 sox -n sine-24dBFS.wav synth 20 sine 1000 gain -24 sox -n sine-30_dBFS.wav synth 20 sine 1000 gain -30
sox sine_-28dBFS.wav sine-28dBFS.wav sine-24dBFS.wav sine-30dBFS.wav sine-30_dBFS.wav testcase-6-channels-5.0.wav -M
loudness scan -l testcase-6-channels-5.0.wav
There are also ready made testfiles available from the EBU website here : http://tech.ebu.ch/webdav/site/tech/shared/testmaterial/ebu-loudness-test-setv03.zip
These are testfiles discussed in EBU documents 3341 and 3342 (documents downloadable here: http://tech.ebu.ch/loudness).
The files in this zip named:
seq-3341-6-5channels-16bit.wav seq-3341-6-6channels-WAVEEX-16bit.wav
should produce the same result -23.0 LUFS. The first one is a 5.0 channel file and the second the same as the first but with a silent .1 channel added as channel number 4.
Possible solution
I suspect the cause of the problem might be that 'loudness' makes a static 'assumption' of which channels are rear channels and which are LFE and this assumption doesn't work with channel counts from 3 to 5.
One of my coworkers took part to the PLOUD group that formulated the EBU R128 standard and I asked him what channel counts and channel orders are supported by EBU R128 and he replied that the standard does not care about channel orders, it only defines that rear channel gain is 1.4 compared to front channels and LFE channels are ignored in calculations. As there is no correct channel order for EBU R128 then the user must tell the loudness meter the type of channels that are processed.
May I humbly suggest a solution to the problem :)
As the calculation only needs to know which channels are front channels, which are rear and which are LFE, then two commandline switches might be used to define the type of all channels, like this:
loudness scan -l surround-channels=5,6 lfe-channel=4 multichannel_5.1.wav
All channel numbers that are not defined in the commandline are front channels.
Commands for processing channel counts from 6 to 3 channels could look like these:
loudness scan -l surround-channels=5,6 lfe-channel=4 multichannel_5.1.wav loudness scan -l surround-channels=4,5 multichannel_5.0.wav loudness scan -l surround-channels=3,4 multichannel_4.0.wav loudness scan -l multichannel_3.0.wav
Sorry that I am not able to send this as a patch, but learning C is still on my todo list and not yet a reality :)
Another possibility to solve the problem might be static channel orders for channels counts from 1 to 6. Flac file format uses very good channel order defaults that I think might work for 90% of the users all the time. Flac site says these channel orders are taken from some SMPTE/ITU-R recommendation, but does not say which document. Flac header definition with default channel orders is here:
http://flac.cvs.sourceforge.net/viewvc/flac/flac/doc/html/format.html#frame_header
The problem with static channel maps is that they won't work in all of the cases.
Mikael Hartzell
Finnish Broadcasting Company (YLE)