LADSPA impulse response plugin.

All your LV2 and LADSPA goodness and more.

Moderators: MattKingUSA, khz

alex stone
Established Member
Posts: 351
Joined: Fri Jun 06, 2008 7:39 am
Has thanked: 67 times
Been thanked: 53 times

LADSPA impulse response plugin.

Post by alex stone »

This is going to sound weird. but hear me out.

I'm working with a few LADSPA plugins at the moment, and it occurred to me how useful an impulse response plugin would be, if you could compile the plugin, with a user supplied IR (or pair left and right). I have a few favourites I use most of the time, but I can't really add them to a LADSPA environment (CLI).

I don't mind having a collection of them, each with an IR I use. Preferably stereo in and out. A LADSPA ir plugin, which will compile with user supplied impulse files, and have some basic controls for delay, early reflections, gain, dry/wet, etc...

Why am I doing this?

I'm trying to get rid of as many GUI's as possible, and save the 'puter cycles for running my large orchestral setup. I'm nearly there, just sorting out the effects. (All hail LADSPA, and other CLI plugins)

So my question is, does such a beast exist, or has ever existed, in our LADSPA world?

User avatar
d.healey
Established Member
Posts: 611
Joined: Fri Sep 22, 2017 8:33 pm
Has thanked: 279 times
Been thanked: 101 times

Re: LADSPA impulse response plugin.

Post by d.healey »

The GUI of a plugin shouldn't eat up any CPU when the GUI is closed. So I wouldn't stress too much about it.

David Healey
YouTube - Free HISE scripting and sample library dev tutorials
Libre Wave - Freedom respecting instruments and effects.
tramp
Established Member
Posts: 2348
Joined: Mon Jul 01, 2013 8:13 am
Has thanked: 9 times
Been thanked: 468 times

Re: LADSPA impulse response plugin.

Post by tramp »

The good old Impulse Convolver from Steve Harris could do that.
https://github.com/swh/ladspa/tree/v0.4.17

You just need to convert your IR-Files to a header file which could be included. There are converter scripts in the Impulses folder to allow you to replace the current implemented files with your own ones.

On the road again.
alex stone
Established Member
Posts: 351
Joined: Fri Jun 06, 2008 7:39 am
Has thanked: 67 times
Been thanked: 53 times

Re: LADSPA impulse response plugin.

Post by alex stone »

d.healey wrote: Sat Feb 03, 2024 10:40 pm

The GUI of a plugin shouldn't eat up any CPU when the GUI is closed. So I wouldn't stress too much about it.

Yep, understood. It's LADSPA format I need though.

alex stone
Established Member
Posts: 351
Joined: Fri Jun 06, 2008 7:39 am
Has thanked: 67 times
Been thanked: 53 times

Re: LADSPA impulse response plugin.

Post by alex stone »

tramp wrote: Sun Feb 04, 2024 5:23 am

The good old Impulse Convolver from Steve Harris could do that.
https://github.com/swh/ladspa/tree/v0.4.17

You just need to convert your IR-Files to a header file which could be included. There are converter scripts in the Impulses folder to allow you to replace the current implemented files with your own ones.

I'll try that. Thanks for the heads up.

EDIT:

After a lot of muddling, I got close. And you were right Tramp. There's a script in the impulses folder that rebuilds the all.h file.

To convert from .wav to .h was a two stage process, but I don't know if it was successful, given the error below. (and because i'm an ordinary user)

$ sox greathall.wav greathall.dat

$ awk '{ print $2 "," }' greathall.dat > greathall.h

The failed compile error

In file included from impulses/all.h:5,
from imp_1199.xml:48:
./impulses/01-greathall.h:1:1: warning: data definition has no type or storage class
1 | Sample,
| ~~
./impulses/01-greathall.h:1:1: warning: type defaults to 'int' in declaration of 'Sample' [-Wimplicit-int]
./impulses/01-greathall.h:2:1: warning: type defaults to 'int' in declaration of 'Channels' [-Wimplicit-int]
2 | Channels,
| ~~~~
./impulses/01-greathall.h:3:1: error: expected identifier or '(' before numeric constant
3 | 0,
| ^
imp_1199.xml: In function 'instantiateImp':
imp_1199.xml:159:2: warning: implicit declaration of function 'mk_imps' [-Wimplicit-function-declaration]
159 | mk_imps(impulse_freq);
| ^ ~~
make[2]: *** [Makefile:1420: imp_1199_la-imp_1199.lo] Error 1
make[2]: Leaving directory '/home/alex/Downloads/git/ladspa'
make[1]: *** [Makefile:1463: all-recursive] Error 1
make[1]: Leaving directory '/home/alex/Downloads/git/ladspa'
make: *** [Makefile:937: all] Error 2

EDIT2: I fixed it. The greathall.h header file needed brackets { };

Compile is successful, installation to home directory/lib/ladspa is complete, and now for testing.

EDIT3:

Testing went badly. From one impulse to multiple impulses, the plugin immediately segfaulted (and pulled everything else down with it)

Worth a try, and I learned a lot.

tramp
Established Member
Posts: 2348
Joined: Mon Jul 01, 2013 8:13 am
Has thanked: 9 times
Been thanked: 468 times

Re: LADSPA impulse response plugin.

Post by tramp »

I've done such stuff a lot. :lol:
Here is a little app which will convert wav files into the needed format for the Impulse Convolver from Steve Harris.

Code: Select all

#include <stdio.h>
#include <iostream>
#include <filesystem>
#include <vector>
#include <string>
#include <algorithm>

using std::min;
// g++ -w -o sfconvert sfconvert.cpp -lsndfile

#include <sndfile.hh>

std::string space2underscore(std::string text) {
    std::replace(text.begin(), text.end(), ' ', '_');
    text.resize (text.size () - 4);
    return text;
}

std::string base_name(std::string const & path) {
    return space2underscore(path.substr(path.find_last_of("/\\") + 1));
}

int main(int argc, char *argv[]) {

if (argc < 2) {
    std::cerr << "Convert a wav file to a C float array\n"
       << "       please give a file path/name to convert\n"
       << "       To trim the output give the numbers of samples to be used\n"
       << "       Example: sfconvert my.wav 256\n" 
       << "       will create a file my.h with a float array of size 256" << std::endl;
    
    return 1;
}
std::string file = argv[1];
int clip = 0;
if (argc > 2) {
    std::string input(argv[2]);
    if (!(std::any_of(input.begin(), input.end(), [](char ch) { return !isdigit(ch); }))) {
        clip = atoi(argv[2]);
    }
}

// struct to hols sound file info
SF_INFO info;
info.format = 0;

// Open the wave file for reading
SNDFILE *sndfile = sf_open(file.c_str(), SFM_READ, &info);

if (!sndfile) {
    std::cerr << "Error: could not open file" << std::endl;
    return 1;
}

std::string name = base_name(file);
std::string fname = name + ".h";
FILE *fp;
if((fp=freopen(fname.c_str(), "w" ,stdout))==NULL) {
    fprintf(stderr,"open failed\n");
    return 1;
}

std::vector<float> samples(info.frames * info.channels);
sf_readf_float(sndfile, &samples[0], info.frames);
sf_close(sndfile);

int trim = (int)samples.size();
if (clip) trim = min(clip, (int)samples.size());

printf( "float %s[] = {\n", name.c_str());
printf( "   ");

int j = 0;
for (int i = 0; i < trim; i++){
    if (i<info.frames-1)
        printf(" %f,", samples[i]);
    else
        printf(" %f", samples[i]);
    ++j;
    if (j>4) {
        printf( "\n   ");
        j = 0;
    }
}
printf( "  };");

fclose(fp);

return 0;
}

Just copy it to a file, save it as sfconvert.cpp and then build it with

Code: Select all

g++ -w -o sfconvert sfconvert.cpp -lsndfile

It could convert the complete file, or, it could as well trim the array to a given number.
The resulting header files should work within the Impulse Convolver from Steve Harris.

On the road again.
User avatar
Daniele71
Established Member
Posts: 67
Joined: Mon Aug 07, 2023 3:02 pm
Location: Italy
Has thanked: 26 times
Been thanked: 14 times
Contact:

Re: LADSPA impulse response plugin.

Post by Daniele71 »

I don't know... what a complication... I'd try lv2-ir plugin. It's simple and not heavy..

My music: Youtube
GeekOsDaw: GeekOsDaw

tramp
Established Member
Posts: 2348
Joined: Mon Jul 01, 2013 8:13 am
Has thanked: 9 times
Been thanked: 468 times

Re: LADSPA impulse response plugin.

Post by tramp »

Daniele71 wrote: Sun Feb 04, 2024 1:44 pm

I don't know... what a complication... I'd try lv2-ir plugin. It's simple and not heavy..

Do you notice that it is about LADSPA, not LV2.

@alex stone Note that the converter is for mono files only as the Impulse Convolver is mono.

On the road again.
alex stone
Established Member
Posts: 351
Joined: Fri Jun 06, 2008 7:39 am
Has thanked: 67 times
Been thanked: 53 times

Re: LADSPA impulse response plugin.

Post by alex stone »

tramp wrote: Sun Feb 04, 2024 1:29 pm

I've done such stuff a lot. :lol:
Here is a little app which will convert wav files into the needed format for the Impulse Convolver from Steve Harris.

Code: Select all

#include <stdio.h>
#include <iostream>
#include <filesystem>
#include <vector>
#include <string>
#include <algorithm>

using std::min;
// g++ -w -o sfconvert sfconvert.cpp -lsndfile

#include <sndfile.hh>

std::string space2underscore(std::string text) {
    std::replace(text.begin(), text.end(), ' ', '_');
    text.resize (text.size () - 4);
    return text;
}

std::string base_name(std::string const & path) {
    return space2underscore(path.substr(path.find_last_of("/\\") + 1));
}

int main(int argc, char *argv[]) {

if (argc < 2) {
    std::cerr << "Convert a wav file to a C float array\n"
       << "       please give a file path/name to convert\n"
       << "       To trim the output give the numbers of samples to be used\n"
       << "       Example: sfconvert my.wav 256\n" 
       << "       will create a file my.h with a float array of size 256" << std::endl;
    
    return 1;
}
std::string file = argv[1];
int clip = 0;
if (argc > 2) {
    std::string input(argv[2]);
    if (!(std::any_of(input.begin(), input.end(), [](char ch) { return !isdigit(ch); }))) {
        clip = atoi(argv[2]);
    }
}

// struct to hols sound file info
SF_INFO info;
info.format = 0;

// Open the wave file for reading
SNDFILE *sndfile = sf_open(file.c_str(), SFM_READ, &info);

if (!sndfile) {
    std::cerr << "Error: could not open file" << std::endl;
    return 1;
}

std::string name = base_name(file);
std::string fname = name + ".h";
FILE *fp;
if((fp=freopen(fname.c_str(), "w" ,stdout))==NULL) {
    fprintf(stderr,"open failed\n");
    return 1;
}

std::vector<float> samples(info.frames * info.channels);
sf_readf_float(sndfile, &samples[0], info.frames);
sf_close(sndfile);

int trim = (int)samples.size();
if (clip) trim = min(clip, (int)samples.size());

printf( "float %s[] = {\n", name.c_str());
printf( "   ");

int j = 0;
for (int i = 0; i < trim; i++){
    if (i<info.frames-1)
        printf(" %f,", samples[i]);
    else
        printf(" %f", samples[i]);
    ++j;
    if (j>4) {
        printf( "\n   ");
        j = 0;
    }
}
printf( "  };");

fclose(fp);

return 0;
}

Just copy it to a file, save it as sfconvert.cpp and then build it with

Code: Select all

g++ -w -o sfconvert sfconvert.cpp -lsndfile

It could convert the complete file, or, it could as well trim the array to a given number.
The resulting header files should work within the Impulse Convolver from Steve Harris.

Thanks for this. I'm off to test.

alex stone
Established Member
Posts: 351
Joined: Fri Jun 06, 2008 7:39 am
Has thanked: 67 times
Been thanked: 53 times

Re: LADSPA impulse response plugin.

Post by alex stone »

Daniele71 wrote: Sun Feb 04, 2024 1:44 pm

I don't know... what a complication... I'd try lv2-ir plugin. It's simple and not heavy..

You can't use LV2 plugins as internal effects in linuxsampler, only ladspa.

alex stone
Established Member
Posts: 351
Joined: Fri Jun 06, 2008 7:39 am
Has thanked: 67 times
Been thanked: 53 times

Re: LADSPA impulse response plugin.

Post by alex stone »

tramp wrote: Sun Feb 04, 2024 2:25 pm
Daniele71 wrote: Sun Feb 04, 2024 1:44 pm

I don't know... what a complication... I'd try lv2-ir plugin. It's simple and not heavy..

Do you notice that it is about LADSPA, not LV2.

@alex stone Note that the converter is for mono files only as the Impulse Convolver is mono.

Understood.

User avatar
Daniele71
Established Member
Posts: 67
Joined: Mon Aug 07, 2023 3:02 pm
Location: Italy
Has thanked: 26 times
Been thanked: 14 times
Contact:

Re: LADSPA impulse response plugin.

Post by Daniele71 »

tramp wrote: Sun Feb 04, 2024 2:25 pm

Do you notice that it is about LADSPA, not LV2.

@alex stone Note that the converter is for mono files only as the Impulse Convolver is mono.

I didn't understand that LADSPA was mandatory, I was thinking of a choice for some resons.. sorry.

My music: Youtube
GeekOsDaw: GeekOsDaw

alex stone
Established Member
Posts: 351
Joined: Fri Jun 06, 2008 7:39 am
Has thanked: 67 times
Been thanked: 53 times

Re: LADSPA impulse response plugin.

Post by alex stone »

tramp wrote: Sun Feb 04, 2024 2:25 pm
Daniele71 wrote: Sun Feb 04, 2024 1:44 pm

I don't know... what a complication... I'd try lv2-ir plugin. It's simple and not heavy..

Do you notice that it is about LADSPA, not LV2.

@alex stone Note that the converter is for mono files only as the Impulse Convolver is mono.

I'm part of the way there, I now have three mono IRs setup and the plugin doesn't complain. The levels are very quiet, so i''m working on those at the moment.

Thanks Tramp. Your little app made the difference, and I appreciate you sharing.

Now to figure out how to add an additional effect chain to each linuxsampler port, so I can run two instances in each audio device port (I have 4 of them in this little test setup), as L-R.

It's been a thoroughly interesting day, and it's not over yet.....!

tramp
Established Member
Posts: 2348
Joined: Mon Jul 01, 2013 8:13 am
Has thanked: 9 times
Been thanked: 468 times

Re: LADSPA impulse response plugin.

Post by tramp »

I've reworked the app a bit, so now it allow to convert stereo files as well. It will generate a header file per channel and apply _L / _R to the filename.
Also it's now possible to apply a gain correction value to adjust the levels. just give a float value to use after the filename.
Here is the advanced app:

Code: Select all

#include <stdio.h>
#include <iostream>
#include <filesystem>
#include <vector>
#include <string>
#include <algorithm>
#include <cmath>

using std::min;
// g++ -w -o sfconvert sfconvert.cpp -lsndfile

#include <sndfile.hh>

std::string space2underscore(std::string text) {
    std::replace(text.begin(), text.end(), ' ', '_');
    text.resize (text.size () - 4);
    return text;
}

std::string base_name(std::string const & path) {
    return space2underscore(path.substr(path.find_last_of("/\\") + 1));
}

int main(int argc, char *argv[]) {

if (argc < 2) {
    std::cerr << "Convert a wav file to a C float array\n"
       << "       please give a file path/name to convert\n"
       << "       To trim the output give the numbers of samples to be used\n"
       << "       Example: sfconvert my.wav 256\n" 
       << "       will create a file my.h with a float array of size 256\n" 
       << "       To apply a gain correction give a (float) value to use\n"
       << "       Example: sfconvert my.wav 0.86\n" 
       << "       will create a file my.h with gain reduced by 0.86\n" 
       << "       both options could be used independent\n" << std::endl;
    
    return 1;
}
std::string file = argv[1];
int clip = 0;
float gain = 1.0;
if (argc > 2) {
    for (int a = 2; a < argc; a++) {
        std::size_t pos{};
        std::string input(argv[a]);
        std::stoi(input, &pos);
        if (pos == input.size()) {
            clip = std::stoi(input);
        } else {
            std::stof(input, &pos);
            if (pos == input.size())
                gain = std::stof(input);
        }
    }
}

// struct to hols sound file info
SF_INFO info;
info.format = 0;

// Open the wave file for reading
SNDFILE *sndfile = sf_open(file.c_str(), SFM_READ, &info);

if (!sndfile) {
    std::cerr << "Error: could not open file" << std::endl;
    return 1;
}


std::vector<float> samples(info.frames * info.channels);
sf_readf_float(sndfile, &samples[0], info.frames * info.channels);
sf_close(sndfile);

int trim = (int)samples.size();
if (clip) trim = min(clip, (int)samples.size());

std::string ch = "";
if (info.channels > 1) ch = "_L";

for (int c = 0; c < info.channels; c++) {
    std::string name = base_name(file) + ch;
    std::string fname = name + ".h";
    FILE *fp;
    if((fp=freopen(fname.c_str(), "w" ,stdout))==NULL) {
        fprintf(stderr,"open failed\n");
        return 1;
    }

    printf( "float %s[] = {\n", name.c_str());
    printf( "   ");

    int j = 0;
    for (int i = 0; i < trim/info.channels; i++){
        if (i<info.frames-1)
            printf(" %f,", samples[i*info.channels+c] * gain);
        else
            printf(" %f", samples[i*info.channels+c] * gain);
        ++j;
        if (j>4) {
            printf( "\n   ");
            j = 0;
        }
    }
    printf( "  };");

    fclose(fp);
    ch = "_R";
}
return 0;
}
On the road again.
alex stone
Established Member
Posts: 351
Joined: Fri Jun 06, 2008 7:39 am
Has thanked: 67 times
Been thanked: 53 times

Re: LADSPA impulse response plugin.

Post by alex stone »

tramp wrote: Mon Feb 05, 2024 9:27 am

I've reworked the app a bit, so now it allow to convert stereo files as well. It will generate a header file per channel and apply _L / _R to the filename.
Also it's now possible to apply a gain correction value to adjust the levels. just give a float value to use after the filename.
Here is the advanced app:

Code: Select all

#include <stdio.h>
#include <iostream>
#include <filesystem>
#include <vector>
#include <string>
#include <algorithm>
#include <cmath>

using std::min;
// g++ -w -o sfconvert sfconvert.cpp -lsndfile

#include <sndfile.hh>

std::string space2underscore(std::string text) {
    std::replace(text.begin(), text.end(), ' ', '_');
    text.resize (text.size () - 4);
    return text;
}

std::string base_name(std::string const & path) {
    return space2underscore(path.substr(path.find_last_of("/\\") + 1));
}

int main(int argc, char *argv[]) {

if (argc < 2) {
    std::cerr << "Convert a wav file to a C float array\n"
       << "       please give a file path/name to convert\n"
       << "       To trim the output give the numbers of samples to be used\n"
       << "       Example: sfconvert my.wav 256\n" 
       << "       will create a file my.h with a float array of size 256\n" 
       << "       To apply a gain correction give a (float) value to use\n"
       << "       Example: sfconvert my.wav 0.86\n" 
       << "       will create a file my.h with gain reduced by 0.86\n" 
       << "       both options could be used independent\n" << std::endl;
    
    return 1;
}
std::string file = argv[1];
int clip = 0;
float gain = 1.0;
if (argc > 2) {
    for (int a = 2; a < argc; a++) {
        std::size_t pos{};
        std::string input(argv[a]);
        std::stoi(input, &pos);
        if (pos == input.size()) {
            clip = std::stoi(input);
        } else {
            std::stof(input, &pos);
            if (pos == input.size())
                gain = std::stof(input);
        }
    }
}

// struct to hols sound file info
SF_INFO info;
info.format = 0;

// Open the wave file for reading
SNDFILE *sndfile = sf_open(file.c_str(), SFM_READ, &info);

if (!sndfile) {
    std::cerr << "Error: could not open file" << std::endl;
    return 1;
}


std::vector<float> samples(info.frames * info.channels);
sf_readf_float(sndfile, &samples[0], info.frames * info.channels);
sf_close(sndfile);

int trim = (int)samples.size();
if (clip) trim = min(clip, (int)samples.size());

std::string ch = "";
if (info.channels > 1) ch = "_L";

for (int c = 0; c < info.channels; c++) {
    std::string name = base_name(file) + ch;
    std::string fname = name + ".h";
    FILE *fp;
    if((fp=freopen(fname.c_str(), "w" ,stdout))==NULL) {
        fprintf(stderr,"open failed\n");
        return 1;
    }

    printf( "float %s[] = {\n", name.c_str());
    printf( "   ");

    int j = 0;
    for (int i = 0; i < trim/info.channels; i++){
        if (i<info.frames-1)
            printf(" %f,", samples[i*info.channels+c] * gain);
        else
            printf(" %f", samples[i*info.channels+c] * gain);
        ++j;
        if (j>4) {
            printf( "\n   ");
            j = 0;
        }
    }
    printf( "  };");

    fclose(fp);
    ch = "_R";
}
return 0;
}

Thanks Tramp. I'll test this afternoon. The Gain value will be useful.

Post Reply