frankl's stereo pages [We support real

[Overview] [Player] [RACE] [LoCo] [DRC]

LoCo: localisation correction


On many stereo recordings the direction of a sound is encoded by volume relations of that sound between the left and right channel (panning). But the relative volume which is needed to localise a sound in a specific direction depends on the frequency of the sound. See this graph for a detailed description of this effect in a certain test setup.

This effect is usually ignored when stereo recordings are mastered. This leads to a blurred localization of instruments and voices during the reproduction of the recording on a stereo system (because instruments and voices produce tones and overtones in a wide range of frequencies). The purpose of this LoCo (localisation correction) project is to correct this effect during stereo playback.

There existed various hardware solutions to reduce this effect. See for example the SHUpHLER or the FRANCINSTIEN devices, the linked pages also give some technical explanation. (In the middle of the SHUpHLER page there is nice picture of an orchestra which is visually blurred in analogy to its sound in a stereo reproduction.) Some multi-way speakers are designed such that the tweeters in a stereo setup have a smaller distance and bass drivers have a larger distance than the mid-range drivers, this can cause a better localization.

In a modern setup much more flexible solutions can be implemented in software. Discussions of such approaches can be found here and here (in German).

Idea of the LoCo filter

We use the software version of the FRANCINSTIEN device mentioned above (a mechanism sometimes called a stereo shuffler). The signals L and R of the left and right stereo channel are transformed into sum M = L+R and difference S = L-R (MS-coding). The original channels can be reconstructed as L = 1/2(M+S) and R=1/2(M-S). When the volume of the S channel is reduced before the reconstruction this leads to a narrower stereo image; and a larger volume yields a wider stereo image.

We want to use this effect, but change the volume of the S channel depending on the frequency, such that the blurred localisation gets corrected. That is, we apply a FIR-filter, called a LoCo filter, to the S channel, get a resulting signal S' and then reconstruct L' = (M+S')/2 and R' = (M-S')/2.

This method can work because of the observation that the needed volume correction for a fixed frequency does not or not much depend on the perceived direction of a sound. For example, one can compute for any of the three curves given in this graph the frequency dependend volume changes of the S channel which are needed to transform the curves into horizontal straight lines; the result is the same for all three curves (directions). This can also be experienced with the program described below.

Nevertheless, for example from the discussions linked to above, we know that different stereo setups need different LoCo filters (different speaker geometry, speaker location, room, ...).

A program to find filter parameters for LoCo

I provide a program to determine in a given stereo setup good parameters for a LoCo filter. The idea of the program is as follows:

A reference tone (~740 Hz) is played and it moves repeatedly in a loop from left to right and back. Then a further tone is added and one has to decide if it moves slower or faster between left and right. Via interactive buttons one can then change the relative volume on the left and right channel of the test tone until the test tone moves synchronously with the reference tone. This procedure has to be repeated with as many test tones of different frequencies as possible (up to about 100 test tones).

When sufficiently many parameters for the LoCo filter are determined one can also compute a corresponding LoCo filter.

The reference and test tones were generated with the help of recorded key strokes of a piano (freely available here) and band pass filters.

Download of program

Prerequisites: To run the program, a computer with a Linux operating system is needed such that it is possible to play sound files from that system on the stereo equipment under consideration (can be a desktop or notebook or a small computer like an Odroid or a Raspberry-Pi).

Here are installation instructions and the archive with the program (Version v0.6, Oct 2018).

How does it sound?

I have used the program provided here to find good LoCo filter parameters for my own stereo system. Then I have tested the generated LoCo filter on many different recordings. I have not found a single example where the filter made the sound of an album worse. On the other hand the improvement of the localization can be spectacular on some recordings. It can unveil details of a recording which are blurred without the LoCo filter, instruments get better separated, the sound of different strings of string instruments or a piano can be distinguished, bass becomes tighter.

By the way: the effect of the LoCo filter is just as strong on sounds in the center of the stereo image than on sounds farther to the left or right.

To find out if you can get a similar effect in your stereo system you are invited to use the tools provided here and to test it yourself. Getting through the parameter finding process needs some effort, but the result may be worth it.

Download of "default" filters

Here are links to high resolution minimal phase filter files which can be used (for example) with brutefir (FLOAT64_LE format) for various sample rates:
FHR-44.1.dbl, FHR-48.dbl, FHR-88.2.dbl, FHR-96.dbl, FHR-192.dbl.

These filters implement the LoCo correction implied by the graph mentioned above. I have used them successfully in my stereo setup for a long time, but the parameter finding program allowed to generate much better filters for my setup.

Example brutefir configuration

One possibility to apply such LoCo filters is with brutefir. Here is an example configuration file (for the case of 192000/32 sounds). If you also use room correction filters you should apply the LoCo filter first, as in this example.

sampling_rate: 192000;
# this is for 262144 taps
filter_length: 16384,16;
overflow_warnings: true;
float_bits: 64;

# the LoCo filter for the difference channel S
coeff "S channel" {
        filename: "/your/path/to/FHR-192.dbl";
        format: "FLOAT64_LE";

# room correction filters for left and right channel
coeff "left_default" {
        filename: "/path/to/roomcorretion/filter/left192.pcm";     
        format: "FLOAT64_LE";
coeff "right_default" {
        filename: "/path/to/roomcorretion/filter/right192.pcm";     
        format: "FLOAT64_LE";

input "left", "right" {
        device: "file" {path: "/dev/stdin";};
        sample: "FLOAT64_LE";   # sample format
        channels: 2;            # number of open channels / which to use
        delay: 0,0;             # delay in samples for each channel
        maxdelay: -1;           # max delay for variable delays
        mute: false,false;      # mute active on startup for each channel

output "left", "right" {
        device: "file" {path: "/dev/stdout";};
        sample: "S32_LE";   # sample format
        channels: 2;        # number of open channels / which to use
        delay: 0,0;         # delay in samples for each channel
        maxdelay: 100;      # max delay for variable delays
        mute: false,false;  # mute active on startup for each channel
        dither: false;      # no dither for 32 bit output

# factor 0.4 to avoid clipping
filter "M direct" {
        from_inputs: "left"//0.4, "right"//0.4;
        to_filters: "left direct", "right direct";
        coeff: -1;
        crossfade: false;

# here 0.8 instead of 0.4 because the filter "in average" multiplies with 0.5
# (one could make 0.8 a bit larger/smaller to obtain a wider/narrower
# stereo image, but better use RACE for this)
filter "S filter" {
        from_inputs: "left"//0.8, "right"//-0.8;
        to_filters: "left direct", "right direct";
        coeff: "S channel";
        crossfade: false;

filter "left direct" {
        from_filters: "M direct"//1.0, "S filter"//1.0;
        to_outputs: "left"/0.0;
        coeff: "left_default";
        crossfade: false;

filter "right direct" {
        from_filters: "M direct"//1.0, "S filter"//-1.0;
        to_outputs: "right"/0.0;
        coeff: "right_default";
        crossfade: false;

Hint, if you don't have suitable filters available but want to test the basic setup in your system: remove or comment the sections starting with coeff for those filters you don't have, and change in the sections starting with filter the value of coeff: to -1 (which means, that the signal remains unchanged).