Dynamic of audio samples

Hi all,
I have some audio samples of single notes played on a piano with “pp” dynamic.
The problem is that the dynamics is not uniform, some notes are slightly louder, others less so. In fact, if I use them to make a chromatic scale (using a MIDI keyboard), I can hear slight accents or gaps.

What is the correct way to make the dynamics of all samples uniform, without distorting the sound?
(If it’s necessary to set a reference dynamic, I could choose the sample that sounds loudest of all, and match all the others to that.)

[Ardour 8.1 on Linux]
Thank you,
a.

I’m assuming you have individual samples in different files and would like to keep them that way?

As you are on Linux, you could install sox and do something like:

for file in *.wav; do sox "$file" "n_$file" norm -0.1; done

Cheers,

Keith

If you load them as separate regions in Ardour you can select them all, right click gain > normalise, and check the box to use the volume across all the selected regions as a reference (I can’t remember it’s actual name but you’ll see it).

Another tool that can do it, in addition to SoX, is Signet - GitHub - SamWindell/Signet: Command-line program for bulk editing audio files on Windows, Linux and Mac

@Majik


`for file in *.wav; do sox "$file" "n_$file" norm -0.1; done`

Yes, this command line works fine. But all .wav files are maximized, for example, here is an original file and immediately below the same file after SoX:

However all files are normalized the same way, so that’s fine.

@DHealey

Thanks, staying in Ardour I have more control than using SoX (it’s very interesting, but I should study how it works…). To choose the correct dBFS value I had to do a bit of experimentation. (Instead I think I understand that setting the LUFS value should be avoided).

However, in both cases (SoX and Ardour) the “leveling” of the dynamics all at once is not perfect, so I have to adjust some notes “by hand” by relying on my ear.
Thank you,
a.

If you are trying to “level” things you need a compressor/expander, not normalization.

Normalization adjusts a given chunk of audio (e.g. a whole file) so that the loudest sample reaches a specified value. It will not “level” the file in the way that I think you want.

By contrast, a compressor/expander will attempt to keep the volume (by some metric, not necessarily LUFS) at a given level, no matter the input level.

Thank you. With a hundred audio samples I need to figure out how to do this…

a.

If levelling (where the volume is the same across the whole file) is really what you want - and I’m not sure you do with decaying samples like piano - there is a tool I commissioned for this exact purpose. It’s CLI so you can run it from a bash script to quickly loop over lots of files. It requires compiling and takes a bit of experimentation to get satisfactory results - GitHub - sadko4u/spike-bender: Loudness maximization tool

What I suspect you want is the volume of the notes to sound consistent across the range of the instrument? If so then I haven’t found an automated tool that can help you. Even if the dB value of two notes is the same, it doesn’t mean they will be perceived at the same volume by the listener.

What I do is normalise my samples, then I map them in my sample player, then I play them back manually with my keyboard and adjust the volume of each one, within the sampler, until they “sound” balanced.

Yes, it is.

Yes, that’s what I’m doing. A bit like to tune a piano, only that I adjust the dynamic by listening to each note.

1 Like

In which case completely disregard my comment about compressor/expander processing. Not relevant for this at all.

I think Paul’s answer is the right one, you need to compress your audio. You can do this in batch using ffmpeg. Here is some good documentation:
https://mim.mbirgin.com/?c=posts&id=172

Here is a script that does both compression via a lv2 plugin then normalization with sox:

REP_IN=./wav-tmp
REP_LV2=./wav-lv2
REP_NORM=./wav-norm

for filename in $REP_IN/*.wav; do
  echo "Convert from: $filename"
  fbname=$(basename "$filename")
  echo "To: $REP_NORM/$fbname"

  #LV2 Calf compressor but u can use the plugins you want 
  ffmpeg -i $filename -af "lv2=http\\\\://calf.sourceforge.net/plugins/Compressor:c=\
  ratio=3|\
  threshold=-12.0" $REP_LV2/$fbname

  #Sox normalization
  sox $REP_LV2/$fbname $REP_NORM/$fbname norm -1
done

too bad, even if I changed the compression level of the plugins in ffmpeg, the signal is not more compressed… See for another solution than ffmpeg for batch processing.
PS: The lv2/ffmpeg solution will work if you build yourself ffmeg with --enable-lv2

If it doesn’t work with lv2, try with the built-in ffmpeg compressor: acompressor
A more détail post here: audio - How to use "compressor" with ffmpeg - Super User

#!/bin/bash

REP_IN=./wav-tmp
REP_LV2=./wav-lv2
REP_NORM=./wav-norm

for filename in $REP_IN/*.wav; do
  echo "Convert from: $filename"
  fbname=$(basename "$filename")
  echo "To: $REP_NORM/$fbname"

  #LV2 Simple compressor 
  ffmpeg -i $filename -af acompressor=level_in=1:threshold=1:ratio=6:attack=200:release=1000:makeup=1:knee=2.82843 $REP_LV2/$fbname

  #NORMALISATION
  sox $REP_LV2/$fbname $REP_NORM/$fbname norm -0.1
done

I would be tempted to level the notes initially, and then apply a compressor to the bus to even out the notes in the final result.

I’m making a lot of assumptions here about your use case.

@atux perhaps you can describe what you are trying to do, and we can give views on potential workflows.

For instance, are you trying to create a virtual instrument using samples, such as a soundfont? In which case you would be trying to adjust a set of samples to load into a soundfont instrument editor like Swami or Polyphone, and this would be a one-off exercise to get the samples set to the right levels.

Or are you trying to do something else?

You briefly mention using MIDI, but in what context?

Why is this a problem? If you are building a soundfont or other virtual instrument, I would have thought this is what you wanted.

As you can see, understanding the context would allow us to help you better.

By the way, in the sox command I gave you, the -0.1 means -0.1 dB. If you want some other level, change the number. For instance, the following will level the files to -3dB:

for file in *.wav; do sox "$file" "n_$file" norm -3; done

Cheers,

Keith

The solution with ffmpeg works, the problem was that the threshold must be between 0 and 1 and not in dB…
This solution works:

#!/bin/bash
REP_IN=./wav-tmp
REP_LV2=./wav-lv2
REP_NORM=./wav-norm

for filename in $REP_IN/*.wav; do
  echo "Conversion de: $filename"
  fbname=$(basename "$filename")
  echo "Vers: $REP_NORM/$fbname".

  #LV2 Calf compressor
  ffmpeg -y -i $filename -af "lv2=http\\\\://calf.sourceforge.net/plugins/Compressor:c=\
  bypass=0.000000|\
  level_in=1.000000|\
  threshold=0.125000|\
  ratio=6.000000|\
  attack=0.000000|\
  release=250.000000|\
  makeup=1.000000|\
  knee=2.828430|\
  detection=0.000000|\
  stereo_link=0.000000|\
  mix=1.000000" $REP_LV2/$fbname

  #Sox norm -3dB
  sox $REP_LV2/$fbname $REP_NORM/$fbname norm -3
done

image

1 Like

Thank you.
How does your script code change if I use “acompressor” instead of “lv2”?
Thank you,
a.

The acompressor solution uses the ffmpeg compressor, the second solution uses the lv2 calf Compressor plugins. They are both compressor-type audio processing which allows the dynamics of the signal to be managed. The important thing is therefore to master the important parameters (Ratio, threshold and the others but these are the main ones). First of all, if you are not familiar with this type of processing, you should do compression tests to familiarize yourself with these tools. Afterwards you will adjust the batch according to what you want to do.

I don’t know if you’re on Linux but if so here’s what you can do (You can probably find an equivalent under other os), you launch the plugins with jalv (A command line host)

jalv.gtk http://calf.sourceforge.net/plugins/Compressor

The plugin launches, you can then run your audio files one after the other to adjust an average level of compression that you will apply to all your files.

Then you save the compressor setting, and you will find the parameters to apply in the script (File → Save).

What you save is in fact a directory containing two files, edit the state.ttl file, you will find the current plugin settings there:

Maybe there’s a misunderstanding… in Ardour I already use compressors of various types.
The problem was that your script gave me this error:

:~/TEST$ ./test
Conversion de: ./wav-tmp/*.wav
Vers: ./wav-norm/*.wav.
ffmpeg version 6.1.1 Copyright (c) 2000-2023 the FFmpeg developers
  built with gcc 11 (Ubuntu 11.4.0-1ubuntu1~22.04)
  configuration: 
  libavutil      58. 29.100 / 58. 29.100
  libavcodec     60. 31.102 / 60. 31.102
  libavformat    60. 16.100 / 60. 16.100
  libavdevice    60.  3.100 / 60.  3.100
  libavfilter     9. 12.100 /  9. 12.100
  libswscale      7.  5.100 /  7.  5.100
  libswresample   4. 12.100 /  4. 12.100
[in#0 @ 0x557f43ea5480] Error opening input: No such file or directory
Error opening input file ./wav-tmp/*.wav.
Error opening input files: No such file or directory
sox FAIL formats: can't open input file `./wav-lv2/*.wav': No such file or directory

Since you wrote to me: : “if lv2 doesn’t work, you can use acompressor”, I asked you: using acompressor (which I already installed), how should the code be modified.
But maybe it has nothing to do with it, I don’t know. You will be able to understand the error I reported above.
Bye,
a.

The script defined these folders and presumes they exist in the folder you’re currently in

REP_IN=./wav-tmp
REP_LV2=./wav-lv2
REP_NORM=./wav-norm

Either modify the script to point where your wav-files are (the REP_IN variable) and where you want to put the normalized ones (REP_NORM) or make sure you have your wavs in ./wav-tmp and that you have a ./wav-norm where ffmpeg can put the normalized files.

OK, now REP_IN, with the correct path, find the .wav files.
But… REP_LV2 = ?
What is it exactly? where do I find the correct path? I have a lot of lv2…I don’t know what to look for.
[Ardour on Linux]

Thanks,
a.

REP_LV2 is used if you want calf compressed files.
sox puts the normalized wavs in REP_NORM and the calf compressed ones are placed in REP_LV2

So either point that variable to an existing folder where you want those files to end up or do an ‘mkdir ./wav-lv2’ before running the script.