ffmpeg-normalize 1.30.0__tar.gz → 1.31.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/CHANGELOG.md +24 -0
  2. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/PKG-INFO +91 -7
  3. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/README.md +66 -6
  4. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize/__main__.py +16 -3
  5. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize/_cmd_utils.py +3 -3
  6. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize/_ffmpeg_normalize.py +3 -0
  7. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize/_media_file.py +8 -4
  8. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize/_streams.py +16 -2
  9. ffmpeg_normalize-1.31.1/ffmpeg_normalize/_version.py +1 -0
  10. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize.egg-info/PKG-INFO +91 -7
  11. ffmpeg_normalize-1.30.0/ffmpeg_normalize/_version.py +0 -1
  12. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/LICENSE +0 -0
  13. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize/__init__.py +0 -0
  14. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize/_errors.py +0 -0
  15. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize/_logger.py +0 -0
  16. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize/py.typed +0 -0
  17. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize.egg-info/SOURCES.txt +0 -0
  18. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize.egg-info/dependency_links.txt +0 -0
  19. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize.egg-info/entry_points.txt +0 -0
  20. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize.egg-info/not-zip-safe +0 -0
  21. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize.egg-info/requires.txt +0 -0
  22. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/ffmpeg_normalize.egg-info/top_level.txt +0 -0
  23. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/setup.cfg +0 -0
  24. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/setup.py +0 -0
  25. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/test/out.mp4 +0 -0
  26. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/test/test.mp4 +0 -0
  27. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/test/test.py +0 -0
  28. {ffmpeg_normalize-1.30.0 → ffmpeg_normalize-1.31.1}/test/test.wav +0 -0
@@ -1,6 +1,30 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## v1.31.1 (2025-02-19)
5
+
6
+ * Fix bitrate setting for libvorbis, fixes #277.
7
+
8
+ * Update issue templates.
9
+
10
+ * Prevent blank issues.
11
+
12
+
13
+ ## v1.31.0 (2024-12-15)
14
+
15
+ * Update docs and completions.
16
+
17
+ * Implement `--auto-lower-loudness-target`
18
+
19
+ * Fix deprecations and mypy --strict errors.
20
+
21
+ * Feat: add completions.
22
+
23
+ * Docs: update explainer.
24
+
25
+ * Docs: update docs to include lower-only.
26
+
27
+
4
28
  ## v1.30.0 (2024-11-22)
5
29
 
6
30
  * Change lower-only message to warning.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ffmpeg_normalize
3
- Version: 1.30.0
3
+ Version: 1.31.1
4
4
  Summary: Normalize audio via ffmpeg
5
5
  Home-page: https://github.com/slhck/ffmpeg-normalize
6
6
  Author: Werner Robitza
@@ -54,6 +54,7 @@ Read on for more info.
54
54
  - [Requirements](#requirements)
55
55
  - [ffmpeg](#ffmpeg)
56
56
  - [Installation](#installation)
57
+ - [Shell Completions](#shell-completions)
57
58
  - [Usage with Docker](#usage-with-docker)
58
59
  - [High LeveL Introduction](#high-level-introduction)
59
60
  - [Basic Usage](#basic-usage)
@@ -130,6 +131,54 @@ Or download this repository, then run `pip3 install .`.
130
131
 
131
132
  To later upgrade to the latest version, run `pip3 install --upgrade ffmpeg-normalize`.
132
133
 
134
+ ### Shell Completions
135
+
136
+ This tool provides shell completions for bash and zsh. To install them:
137
+
138
+ <!--
139
+ Note to self: Generate the shtab ones with:
140
+
141
+ shtab --shell=bash -u ffmpeg_normalize.__main__.create_parser > completions/ffmpeg-normalize-shtab.bash
142
+ shtab --shell=zsh -u ffmpeg_normalize.__main__.create_parser > completions/ffmpeg-normalize-shtab.zsh
143
+
144
+ but these are not properly working yet.
145
+ -->
146
+
147
+ #### Bash
148
+
149
+ If you have [`bash-completion`](https://github.com/scop/bash-completion) installed, you can just copy your new completion script to the `/usr/local/etc/bash_completion.d` directory.
150
+
151
+ ```bash
152
+ curl -L https://raw.githubusercontent.com/slhck/ffmpeg-normalize/master/completions/ffmpeg-normalize-completion.bash \
153
+ -o /usr/local/etc/bash_completion.d/ffmpeg-normalize
154
+ ```
155
+
156
+ Without bash-completion, you can manually install the completion script:
157
+
158
+ ```bash
159
+ # create completions directory if it doesn't exist
160
+ mkdir -p ~/.bash_completions.d
161
+
162
+ # download and install completion script
163
+ curl -L https://raw.githubusercontent.com/slhck/ffmpeg-normalize/master/completions/ffmpeg-normalize-completion.bash \
164
+ -o ~/.bash_completions.d/ffmpeg-normalize
165
+
166
+ # source it in your ~/.bashrc
167
+ echo 'source ~/.bash_completions.d/ffmpeg-normalize' >> ~/.bashrc
168
+ ```
169
+
170
+ #### Zsh
171
+
172
+ Download the completion script and place it in the default `site-functions` directory:
173
+
174
+ ```bash
175
+ curl -L https://raw.githubusercontent.com/slhck/ffmpeg-normalize/master/completions/ffmpeg-normalize.zsh \
176
+ -o /usr/local/share/zsh/site-functions/
177
+ ```
178
+
179
+ You may choose any other directory that is in your `$FPATH` variable.
180
+ Make sure your `.zshrc` file contains `autoload -Uz compinit && compinit`.
181
+
133
182
  ## Usage with Docker
134
183
 
135
184
  You can use the pre-built image from Docker Hub:
@@ -281,10 +330,7 @@ For more information on the options (`[options]`) available, run `ffmpeg-normali
281
330
 
282
331
  - `--keep-lra-above-loudness-range-target`: Keep input loudness range above loudness range target.
283
332
 
284
- - `LOUDNESS_RANGE_TARGET` for input loudness range `<= LOUDNESS_RANGE_TARGET` or
285
- - keep input loudness range target above `LOUDNESS_RANGE_TARGET`.
286
-
287
- as alternative to `--keep-loudness-range-target` to allow for linear normalization.
333
+ Can be used as an alternative to `--keep-loudness-range-target` to allow for linear normalization.
288
334
 
289
335
  - `-tp TRUE_PEAK, --true-peak TRUE_PEAK`: EBU Maximum True Peak in dBTP (default: -2.0).
290
336
 
@@ -296,6 +342,16 @@ For more information on the options (`[options]`) available, run `ffmpeg-normali
296
342
 
297
343
  Range is -99.0 - +99.0.
298
344
 
345
+ - `--lower-only`: Whether the audio should not increase in loudness.
346
+
347
+ If the measured loudness from the first pass is lower than the target loudness then normalization pass will be skipped for the measured audio source.
348
+
349
+ - `--auto-lower-loudness-target`: Automatically lower EBU Integrated Loudness Target.
350
+
351
+ Automatically lower EBU Integrated Loudness Target to prevent falling back to dynamic filtering.
352
+
353
+ Makes sure target loudness is lower than measured loudness minus peak loudness (input_i - input_tp) by a small amount.
354
+
299
355
  - `--dual-mono`: Treat mono input files as "dual-mono".
300
356
 
301
357
  If a mono file is intended for playback on a stereo system, its EBU R128 measurement will be perceptually incorrect. If set, this option will compensate for this effect. Multi-channel input files are not affected by this option.
@@ -304,7 +360,7 @@ For more information on the options (`[options]`) available, run `ffmpeg-normali
304
360
 
305
361
  Instead of applying linear EBU R128 normalization, choose a dynamic normalization. This is not usually recommended.
306
362
 
307
- Dynamic mode will automatically change the sample rate to 192 kHz. Use -ar/--sample-rate to specify a different output sample rate.
363
+ Dynamic mode will automatically change the sample rate to 192 kHz. Use `-ar`/`--sample-rate` to specify a different output sample rate.
308
364
 
309
365
  ### Audio Encoding
310
366
 
@@ -440,7 +496,11 @@ For most cases, linear mode is recommended. Dynamic mode should only be used whe
440
496
 
441
497
  * When the required gain adjustment to meet the integrated loudness target would result in the true peak exceeding the specified true peak limit. This is because linear processing alone cannot reduce peaks without affecting the entire signal. For example, if a file needs to be amplified by 6 dB to reach the target integrated loudness, but doing so would push the true peak above the specified limit, the filter might switch to dynamic mode to handle this situation. If your content allows for it, you can increase the true peak target to give more headroom for linear processing. If you're consistently running into true peak issues, you might also consider lowering your target integrated loudness level.
442
498
 
443
- At this time, the `loudnorm` filter in ffmpeg does not provide a way to force linear mode when the input loudness range exceeds the target or when the true peak would be exceeded. The `--keep-loudness-range-target` option can be used to keep the input loudness range target above the specified target, but it will not force linear mode in all cases. We are working on a solution to handle this automatically!
499
+ At this time, the `loudnorm` filter in ffmpeg does not provide a way to force linear mode when the input loudness range exceeds the target or when the true peak would be exceeded. There are some options to mitigate this:
500
+
501
+ - The `--keep-lra-above-loudness-range-target` option can be used to keep the input loudness range above the specified target, but it will not force linear mode in all cases.
502
+ - Similarly, the `--keep-loudness-range-target` option can be used to keep the input loudness range target.
503
+ - The `--lower-only` option can be used to skip the normalization pass completely if the measured loudness is lower than the target loudness.
444
504
 
445
505
  ### The program doesn't work because the "loudnorm" filter can't be found
446
506
 
@@ -607,6 +667,30 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
607
667
  # Changelog
608
668
 
609
669
 
670
+ ## v1.31.1 (2025-02-19)
671
+
672
+ * Fix bitrate setting for libvorbis, fixes #277.
673
+
674
+ * Update issue templates.
675
+
676
+ * Prevent blank issues.
677
+
678
+
679
+ ## v1.31.0 (2024-12-15)
680
+
681
+ * Update docs and completions.
682
+
683
+ * Implement `--auto-lower-loudness-target`
684
+
685
+ * Fix deprecations and mypy --strict errors.
686
+
687
+ * Feat: add completions.
688
+
689
+ * Docs: update explainer.
690
+
691
+ * Docs: update docs to include lower-only.
692
+
693
+
610
694
  ## v1.30.0 (2024-11-22)
611
695
 
612
696
  * Change lower-only message to warning.
@@ -28,6 +28,7 @@ Read on for more info.
28
28
  - [Requirements](#requirements)
29
29
  - [ffmpeg](#ffmpeg)
30
30
  - [Installation](#installation)
31
+ - [Shell Completions](#shell-completions)
31
32
  - [Usage with Docker](#usage-with-docker)
32
33
  - [High LeveL Introduction](#high-level-introduction)
33
34
  - [Basic Usage](#basic-usage)
@@ -104,6 +105,54 @@ Or download this repository, then run `pip3 install .`.
104
105
 
105
106
  To later upgrade to the latest version, run `pip3 install --upgrade ffmpeg-normalize`.
106
107
 
108
+ ### Shell Completions
109
+
110
+ This tool provides shell completions for bash and zsh. To install them:
111
+
112
+ <!--
113
+ Note to self: Generate the shtab ones with:
114
+
115
+ shtab --shell=bash -u ffmpeg_normalize.__main__.create_parser > completions/ffmpeg-normalize-shtab.bash
116
+ shtab --shell=zsh -u ffmpeg_normalize.__main__.create_parser > completions/ffmpeg-normalize-shtab.zsh
117
+
118
+ but these are not properly working yet.
119
+ -->
120
+
121
+ #### Bash
122
+
123
+ If you have [`bash-completion`](https://github.com/scop/bash-completion) installed, you can just copy your new completion script to the `/usr/local/etc/bash_completion.d` directory.
124
+
125
+ ```bash
126
+ curl -L https://raw.githubusercontent.com/slhck/ffmpeg-normalize/master/completions/ffmpeg-normalize-completion.bash \
127
+ -o /usr/local/etc/bash_completion.d/ffmpeg-normalize
128
+ ```
129
+
130
+ Without bash-completion, you can manually install the completion script:
131
+
132
+ ```bash
133
+ # create completions directory if it doesn't exist
134
+ mkdir -p ~/.bash_completions.d
135
+
136
+ # download and install completion script
137
+ curl -L https://raw.githubusercontent.com/slhck/ffmpeg-normalize/master/completions/ffmpeg-normalize-completion.bash \
138
+ -o ~/.bash_completions.d/ffmpeg-normalize
139
+
140
+ # source it in your ~/.bashrc
141
+ echo 'source ~/.bash_completions.d/ffmpeg-normalize' >> ~/.bashrc
142
+ ```
143
+
144
+ #### Zsh
145
+
146
+ Download the completion script and place it in the default `site-functions` directory:
147
+
148
+ ```bash
149
+ curl -L https://raw.githubusercontent.com/slhck/ffmpeg-normalize/master/completions/ffmpeg-normalize.zsh \
150
+ -o /usr/local/share/zsh/site-functions/
151
+ ```
152
+
153
+ You may choose any other directory that is in your `$FPATH` variable.
154
+ Make sure your `.zshrc` file contains `autoload -Uz compinit && compinit`.
155
+
107
156
  ## Usage with Docker
108
157
 
109
158
  You can use the pre-built image from Docker Hub:
@@ -255,10 +304,7 @@ For more information on the options (`[options]`) available, run `ffmpeg-normali
255
304
 
256
305
  - `--keep-lra-above-loudness-range-target`: Keep input loudness range above loudness range target.
257
306
 
258
- - `LOUDNESS_RANGE_TARGET` for input loudness range `<= LOUDNESS_RANGE_TARGET` or
259
- - keep input loudness range target above `LOUDNESS_RANGE_TARGET`.
260
-
261
- as alternative to `--keep-loudness-range-target` to allow for linear normalization.
307
+ Can be used as an alternative to `--keep-loudness-range-target` to allow for linear normalization.
262
308
 
263
309
  - `-tp TRUE_PEAK, --true-peak TRUE_PEAK`: EBU Maximum True Peak in dBTP (default: -2.0).
264
310
 
@@ -270,6 +316,16 @@ For more information on the options (`[options]`) available, run `ffmpeg-normali
270
316
 
271
317
  Range is -99.0 - +99.0.
272
318
 
319
+ - `--lower-only`: Whether the audio should not increase in loudness.
320
+
321
+ If the measured loudness from the first pass is lower than the target loudness then normalization pass will be skipped for the measured audio source.
322
+
323
+ - `--auto-lower-loudness-target`: Automatically lower EBU Integrated Loudness Target.
324
+
325
+ Automatically lower EBU Integrated Loudness Target to prevent falling back to dynamic filtering.
326
+
327
+ Makes sure target loudness is lower than measured loudness minus peak loudness (input_i - input_tp) by a small amount.
328
+
273
329
  - `--dual-mono`: Treat mono input files as "dual-mono".
274
330
 
275
331
  If a mono file is intended for playback on a stereo system, its EBU R128 measurement will be perceptually incorrect. If set, this option will compensate for this effect. Multi-channel input files are not affected by this option.
@@ -278,7 +334,7 @@ For more information on the options (`[options]`) available, run `ffmpeg-normali
278
334
 
279
335
  Instead of applying linear EBU R128 normalization, choose a dynamic normalization. This is not usually recommended.
280
336
 
281
- Dynamic mode will automatically change the sample rate to 192 kHz. Use -ar/--sample-rate to specify a different output sample rate.
337
+ Dynamic mode will automatically change the sample rate to 192 kHz. Use `-ar`/`--sample-rate` to specify a different output sample rate.
282
338
 
283
339
  ### Audio Encoding
284
340
 
@@ -414,7 +470,11 @@ For most cases, linear mode is recommended. Dynamic mode should only be used whe
414
470
 
415
471
  * When the required gain adjustment to meet the integrated loudness target would result in the true peak exceeding the specified true peak limit. This is because linear processing alone cannot reduce peaks without affecting the entire signal. For example, if a file needs to be amplified by 6 dB to reach the target integrated loudness, but doing so would push the true peak above the specified limit, the filter might switch to dynamic mode to handle this situation. If your content allows for it, you can increase the true peak target to give more headroom for linear processing. If you're consistently running into true peak issues, you might also consider lowering your target integrated loudness level.
416
472
 
417
- At this time, the `loudnorm` filter in ffmpeg does not provide a way to force linear mode when the input loudness range exceeds the target or when the true peak would be exceeded. The `--keep-loudness-range-target` option can be used to keep the input loudness range target above the specified target, but it will not force linear mode in all cases. We are working on a solution to handle this automatically!
473
+ At this time, the `loudnorm` filter in ffmpeg does not provide a way to force linear mode when the input loudness range exceeds the target or when the true peak would be exceeded. There are some options to mitigate this:
474
+
475
+ - The `--keep-lra-above-loudness-range-target` option can be used to keep the input loudness range above the specified target, but it will not force linear mode in all cases.
476
+ - Similarly, the `--keep-loudness-range-target` option can be used to keep the input loudness range target.
477
+ - The `--lower-only` option can be used to skip the normalization pass completely if the measured loudness is lower than the target loudness.
418
478
 
419
479
  ### The program doesn't work because the "loudnorm" filter can't be found
420
480
 
@@ -201,9 +201,7 @@ def create_parser() -> argparse.ArgumentParser:
201
201
  help=textwrap.dedent(
202
202
  """\
203
203
  Keep input loudness range above loudness range target.
204
- - `LOUDNESS_RANGE_TARGET` for input loudness range `<= LOUDNESS_RANGE_TARGET` or
205
- - keep input loudness range target above `LOUDNESS_RANGE_TARGET`.
206
- as alternative to `--keep-loudness-range-target` to allow for linear normalization.
204
+ Can be used as an alternative to `--keep-loudness-range-target` to allow for linear normalization.
207
205
  """
208
206
  ),
209
207
  )
@@ -249,6 +247,20 @@ def create_parser() -> argparse.ArgumentParser:
249
247
  ),
250
248
  )
251
249
 
250
+ group_ebu.add_argument(
251
+ "--auto-lower-loudness-target",
252
+ action="store_true",
253
+ help=textwrap.dedent(
254
+ """\
255
+ Automatically lower EBU Integrated Loudness Target to prevent falling
256
+ back to dynamic filtering.
257
+
258
+ Makes sure target loudness is lower than measured loudness minus peak
259
+ loudness (input_i - input_tp) by a small amount (0.1 LUFS).
260
+ """
261
+ ),
262
+ )
263
+
252
264
  group_ebu.add_argument(
253
265
  "--dual-mono",
254
266
  action="store_true",
@@ -529,6 +541,7 @@ def main() -> None:
529
541
  true_peak=cli_args.true_peak,
530
542
  offset=cli_args.offset,
531
543
  lower_only=cli_args.lower_only,
544
+ auto_lower_loudness_target=cli_args.auto_lower_loudness_target,
532
545
  dual_mono=cli_args.dual_mono,
533
546
  dynamic=cli_args.dynamic,
534
547
  audio_codec=cli_args.audio_codec,
@@ -7,7 +7,7 @@ import shlex
7
7
  import subprocess
8
8
  from platform import system
9
9
  from shutil import which
10
- from typing import Iterator
10
+ from typing import Iterator, Any
11
11
 
12
12
  from ffmpeg_progress_yield import FfmpegProgress
13
13
 
@@ -128,12 +128,12 @@ class CommandRunner:
128
128
  return self.output
129
129
 
130
130
 
131
- def dict_to_filter_opts(opts: dict[str, object]) -> str:
131
+ def dict_to_filter_opts(opts: dict[str, Any]) -> str:
132
132
  """
133
133
  Convert a dictionary to a ffmpeg filter option string
134
134
 
135
135
  Args:
136
- opts (dict[str, object]): Dictionary of options
136
+ opts (dict[str, Any]): Dictionary of options
137
137
 
138
138
  Returns:
139
139
  str: Filter option string
@@ -61,6 +61,7 @@ class FFmpegNormalize:
61
61
  true_peak (float, optional): True peak. Defaults to -2.0.
62
62
  offset (float, optional): Offset. Defaults to 0.0.
63
63
  lower_only (bool, optional): Whether the audio should not increase in loudness. Defaults to False.
64
+ auto_lower_loudness_target (bool, optional): Automatically lower EBU Integrated Loudness Target.
64
65
  dual_mono (bool, optional): Dual mono. Defaults to False.
65
66
  dynamic (bool, optional): Dynamic. Defaults to False.
66
67
  audio_codec (str, optional): Audio codec. Defaults to "pcm_s16le".
@@ -98,6 +99,7 @@ class FFmpegNormalize:
98
99
  true_peak: float = -2.0,
99
100
  offset: float = 0.0,
100
101
  lower_only: bool = False,
102
+ auto_lower_loudness_target: bool = False,
101
103
  dual_mono: bool = False,
102
104
  dynamic: bool = False,
103
105
  audio_codec: str = "pcm_s16le",
@@ -169,6 +171,7 @@ class FFmpegNormalize:
169
171
  self.true_peak = check_range(true_peak, -9, 0, name="true_peak")
170
172
  self.offset = check_range(offset, -99, 99, name="offset")
171
173
  self.lower_only = lower_only
174
+ self.auto_lower_loudness_target = auto_lower_loudness_target
172
175
 
173
176
  # Ensure library user is passing correct types
174
177
  assert isinstance(dual_mono, bool), "dual_mono must be bool"
@@ -177,8 +177,7 @@ class MediaFile:
177
177
  and len(self.streams["audio"].values()) > 1
178
178
  ):
179
179
  _logger.warning(
180
- "Output file only supports one stream. "
181
- "Keeping only first audio stream."
180
+ "Output file only supports one stream. Keeping only first audio stream."
182
181
  )
183
182
  first_stream = list(self.streams["audio"].values())[0]
184
183
  self.streams["audio"] = {first_stream.stream_id: first_stream}
@@ -281,7 +280,7 @@ class MediaFile:
281
280
  skip_normalization = True
282
281
 
283
282
  if skip_normalization:
284
- _logger.warn(
283
+ _logger.warning(
285
284
  f"Stream {audio_stream.stream_id} had measured input loudness lower than target, skipping normalization."
286
285
  )
287
286
  normalization_filter = "acopy"
@@ -390,7 +389,12 @@ class MediaFile:
390
389
 
391
390
  # other audio options (if any)
392
391
  if self.ffmpeg_normalize.audio_bitrate:
393
- cmd.extend(["-b:a", str(self.ffmpeg_normalize.audio_bitrate)])
392
+ if self.ffmpeg_normalize.audio_codec == "libvorbis":
393
+ # libvorbis takes just a "-b" option, for some reason
394
+ # https://github.com/slhck/ffmpeg-normalize/issues/277
395
+ cmd.extend(["-b", str(self.ffmpeg_normalize.audio_bitrate)])
396
+ else:
397
+ cmd.extend(["-b:a", str(self.ffmpeg_normalize.audio_bitrate)])
394
398
  if self.ffmpeg_normalize.sample_rate:
395
399
  cmd.extend(["-ar", str(self.ffmpeg_normalize.sample_rate)])
396
400
  if self.ffmpeg_normalize.audio_channels:
@@ -167,7 +167,7 @@ class AudioStream(MediaStream):
167
167
  }
168
168
  return stats
169
169
 
170
- def set_second_pass_stats(self, stats: EbuLoudnessStatistics):
170
+ def set_second_pass_stats(self, stats: EbuLoudnessStatistics) -> None:
171
171
  """
172
172
  Set the EBU loudness statistics for the second pass.
173
173
 
@@ -481,10 +481,24 @@ class AudioStream(MediaStream):
481
481
  "Specify -ar/--sample-rate to override it."
482
482
  )
483
483
 
484
+ target_level = self.ffmpeg_normalize.target_level
485
+ if self.ffmpeg_normalize.auto_lower_loudness_target:
486
+ safe_target = (
487
+ self.loudness_statistics["ebu_pass1"]["input_i"]
488
+ - self.loudness_statistics["ebu_pass1"]["input_tp"]
489
+ + self.ffmpeg_normalize.true_peak
490
+ - 0.1
491
+ )
492
+ if safe_target < self.ffmpeg_normalize.target_level:
493
+ target_level = safe_target
494
+ _logger.warning(
495
+ f"Using loudness target {target_level} because --auto-lower-loudness-target given.",
496
+ )
497
+
484
498
  stats = self.loudness_statistics["ebu_pass1"]
485
499
 
486
500
  opts = {
487
- "i": self.media_file.ffmpeg_normalize.target_level,
501
+ "i": target_level,
488
502
  "lra": self.media_file.ffmpeg_normalize.loudness_range_target,
489
503
  "tp": self.media_file.ffmpeg_normalize.true_peak,
490
504
  "offset": self._constrain(
@@ -0,0 +1 @@
1
+ __version__ = "1.31.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ffmpeg-normalize
3
- Version: 1.30.0
3
+ Version: 1.31.1
4
4
  Summary: Normalize audio via ffmpeg
5
5
  Home-page: https://github.com/slhck/ffmpeg-normalize
6
6
  Author: Werner Robitza
@@ -54,6 +54,7 @@ Read on for more info.
54
54
  - [Requirements](#requirements)
55
55
  - [ffmpeg](#ffmpeg)
56
56
  - [Installation](#installation)
57
+ - [Shell Completions](#shell-completions)
57
58
  - [Usage with Docker](#usage-with-docker)
58
59
  - [High LeveL Introduction](#high-level-introduction)
59
60
  - [Basic Usage](#basic-usage)
@@ -130,6 +131,54 @@ Or download this repository, then run `pip3 install .`.
130
131
 
131
132
  To later upgrade to the latest version, run `pip3 install --upgrade ffmpeg-normalize`.
132
133
 
134
+ ### Shell Completions
135
+
136
+ This tool provides shell completions for bash and zsh. To install them:
137
+
138
+ <!--
139
+ Note to self: Generate the shtab ones with:
140
+
141
+ shtab --shell=bash -u ffmpeg_normalize.__main__.create_parser > completions/ffmpeg-normalize-shtab.bash
142
+ shtab --shell=zsh -u ffmpeg_normalize.__main__.create_parser > completions/ffmpeg-normalize-shtab.zsh
143
+
144
+ but these are not properly working yet.
145
+ -->
146
+
147
+ #### Bash
148
+
149
+ If you have [`bash-completion`](https://github.com/scop/bash-completion) installed, you can just copy your new completion script to the `/usr/local/etc/bash_completion.d` directory.
150
+
151
+ ```bash
152
+ curl -L https://raw.githubusercontent.com/slhck/ffmpeg-normalize/master/completions/ffmpeg-normalize-completion.bash \
153
+ -o /usr/local/etc/bash_completion.d/ffmpeg-normalize
154
+ ```
155
+
156
+ Without bash-completion, you can manually install the completion script:
157
+
158
+ ```bash
159
+ # create completions directory if it doesn't exist
160
+ mkdir -p ~/.bash_completions.d
161
+
162
+ # download and install completion script
163
+ curl -L https://raw.githubusercontent.com/slhck/ffmpeg-normalize/master/completions/ffmpeg-normalize-completion.bash \
164
+ -o ~/.bash_completions.d/ffmpeg-normalize
165
+
166
+ # source it in your ~/.bashrc
167
+ echo 'source ~/.bash_completions.d/ffmpeg-normalize' >> ~/.bashrc
168
+ ```
169
+
170
+ #### Zsh
171
+
172
+ Download the completion script and place it in the default `site-functions` directory:
173
+
174
+ ```bash
175
+ curl -L https://raw.githubusercontent.com/slhck/ffmpeg-normalize/master/completions/ffmpeg-normalize.zsh \
176
+ -o /usr/local/share/zsh/site-functions/
177
+ ```
178
+
179
+ You may choose any other directory that is in your `$FPATH` variable.
180
+ Make sure your `.zshrc` file contains `autoload -Uz compinit && compinit`.
181
+
133
182
  ## Usage with Docker
134
183
 
135
184
  You can use the pre-built image from Docker Hub:
@@ -281,10 +330,7 @@ For more information on the options (`[options]`) available, run `ffmpeg-normali
281
330
 
282
331
  - `--keep-lra-above-loudness-range-target`: Keep input loudness range above loudness range target.
283
332
 
284
- - `LOUDNESS_RANGE_TARGET` for input loudness range `<= LOUDNESS_RANGE_TARGET` or
285
- - keep input loudness range target above `LOUDNESS_RANGE_TARGET`.
286
-
287
- as alternative to `--keep-loudness-range-target` to allow for linear normalization.
333
+ Can be used as an alternative to `--keep-loudness-range-target` to allow for linear normalization.
288
334
 
289
335
  - `-tp TRUE_PEAK, --true-peak TRUE_PEAK`: EBU Maximum True Peak in dBTP (default: -2.0).
290
336
 
@@ -296,6 +342,16 @@ For more information on the options (`[options]`) available, run `ffmpeg-normali
296
342
 
297
343
  Range is -99.0 - +99.0.
298
344
 
345
+ - `--lower-only`: Whether the audio should not increase in loudness.
346
+
347
+ If the measured loudness from the first pass is lower than the target loudness then normalization pass will be skipped for the measured audio source.
348
+
349
+ - `--auto-lower-loudness-target`: Automatically lower EBU Integrated Loudness Target.
350
+
351
+ Automatically lower EBU Integrated Loudness Target to prevent falling back to dynamic filtering.
352
+
353
+ Makes sure target loudness is lower than measured loudness minus peak loudness (input_i - input_tp) by a small amount.
354
+
299
355
  - `--dual-mono`: Treat mono input files as "dual-mono".
300
356
 
301
357
  If a mono file is intended for playback on a stereo system, its EBU R128 measurement will be perceptually incorrect. If set, this option will compensate for this effect. Multi-channel input files are not affected by this option.
@@ -304,7 +360,7 @@ For more information on the options (`[options]`) available, run `ffmpeg-normali
304
360
 
305
361
  Instead of applying linear EBU R128 normalization, choose a dynamic normalization. This is not usually recommended.
306
362
 
307
- Dynamic mode will automatically change the sample rate to 192 kHz. Use -ar/--sample-rate to specify a different output sample rate.
363
+ Dynamic mode will automatically change the sample rate to 192 kHz. Use `-ar`/`--sample-rate` to specify a different output sample rate.
308
364
 
309
365
  ### Audio Encoding
310
366
 
@@ -440,7 +496,11 @@ For most cases, linear mode is recommended. Dynamic mode should only be used whe
440
496
 
441
497
  * When the required gain adjustment to meet the integrated loudness target would result in the true peak exceeding the specified true peak limit. This is because linear processing alone cannot reduce peaks without affecting the entire signal. For example, if a file needs to be amplified by 6 dB to reach the target integrated loudness, but doing so would push the true peak above the specified limit, the filter might switch to dynamic mode to handle this situation. If your content allows for it, you can increase the true peak target to give more headroom for linear processing. If you're consistently running into true peak issues, you might also consider lowering your target integrated loudness level.
442
498
 
443
- At this time, the `loudnorm` filter in ffmpeg does not provide a way to force linear mode when the input loudness range exceeds the target or when the true peak would be exceeded. The `--keep-loudness-range-target` option can be used to keep the input loudness range target above the specified target, but it will not force linear mode in all cases. We are working on a solution to handle this automatically!
499
+ At this time, the `loudnorm` filter in ffmpeg does not provide a way to force linear mode when the input loudness range exceeds the target or when the true peak would be exceeded. There are some options to mitigate this:
500
+
501
+ - The `--keep-lra-above-loudness-range-target` option can be used to keep the input loudness range above the specified target, but it will not force linear mode in all cases.
502
+ - Similarly, the `--keep-loudness-range-target` option can be used to keep the input loudness range target.
503
+ - The `--lower-only` option can be used to skip the normalization pass completely if the measured loudness is lower than the target loudness.
444
504
 
445
505
  ### The program doesn't work because the "loudnorm" filter can't be found
446
506
 
@@ -607,6 +667,30 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
607
667
  # Changelog
608
668
 
609
669
 
670
+ ## v1.31.1 (2025-02-19)
671
+
672
+ * Fix bitrate setting for libvorbis, fixes #277.
673
+
674
+ * Update issue templates.
675
+
676
+ * Prevent blank issues.
677
+
678
+
679
+ ## v1.31.0 (2024-12-15)
680
+
681
+ * Update docs and completions.
682
+
683
+ * Implement `--auto-lower-loudness-target`
684
+
685
+ * Fix deprecations and mypy --strict errors.
686
+
687
+ * Feat: add completions.
688
+
689
+ * Docs: update explainer.
690
+
691
+ * Docs: update docs to include lower-only.
692
+
693
+
610
694
  ## v1.30.0 (2024-11-22)
611
695
 
612
696
  * Change lower-only message to warning.
@@ -1 +0,0 @@
1
- __version__ = "1.30.0"