Hi Fons,
Thanks for your useful and constructive comments. I have updated the
jack-gtk.cpp architecture file with your suggested main. It provides a
flexible solution that should make both the auto-connection and
manual-connection people happy ;-) Moreover it solves the unique name
problem.
Fons Adriaensen a écrit :
Hi Yann,
Here is a quick solution using Faust :
...
A fully functional jack application can be easily generated using the
faust2jack command or by pasting the above code in the online faust
compiler (
http://faust.grame.fr). The performances on my Vaio laptop
(Intel
Core 2 CPU T7400 @ 2.16GHz) is approximately of 2%.
Many thanks for this. I'm very happy you point me back
to Faust, as despite the comments below I am really
impressed by what it achieves. Enough to organise a
demo yesterday afternoon for two of the researchers
I work with. They are not programmers, but both will
start learning to use Faust.
Great ! Benvenuti ! If they have programming questions (I am sure pretty
they will have some at the beginning ;-)) they are welcome, as well as
suggestions and contributions.
Here are my comments, some of them posted before.
1. The auto-connection issue. If I would install (or
some day, update) Faust at home or at my normal working
place, build the examples to test the installation, and
run 'osc', this would send a continuous, maximum level,
1kHz signal to a power amp feeding an HF speaker that
doesn't much like signals of this frequency, and even
less at a power level at least 20 dB above its maximum
continuous rating. It would be destroyed instantly, and
I would be happy to escape without permanent hearing
damage. Please don't assume you can safely connect to
the first N physical ports. At least make it an option,
and one that needs explicit user action to be enabled.
The code attached below solves this.
2. It's not possible to run a more than one instance
of a JACK client since all instances have the same
name, there is no -name option, and they don't use
the API that automatically generates unique names.
This also is solved by the code attached below.
As I said above I have update the CVS with the revised jack-gtk.cpp
architecture file. The faust server is not updated yet, but it will be
soon...
3. All the recursive filters I tested will generate
denormals if the input is stopped or disconnected.
On Intel systems this can easily push the CPU load
up to crash levels. Solving this requires changes
to all the Faust sources defining these filters.
BTW, some other systems, e.g. SC3, have the same
problem.
This is a real problem but it should be solved on modern intel cpu by
enabling the FTZ mode.
4. The fdelay() functions are not usable for the
application I posted. All of them have significant
HF loss if the delay is not integer, and as a result,
will generate amplitude and maybe also phase modulation
if the delay value is not static. Strangely, fdelay4()
is not any better than fdelay2(), and fdelay3() is even
worse.
Is fdelay *that* bad or do you have a very demanding application ? For the worst
cases (x.5 delays) the amplitude response for fdelay4 is down about 2 dB at 15 kHz and 7
dB at 20 kHz (for a 44.1 kHz sampling rate).
This is consistent with your observed 6 dB drop at 20 kHz for a 48 kHz sampling rate. But
please note that all the gain drop is concentrated at very high frequencies. Moreover,
with a sampling rate of 96 kHz instead of 48 KHz, the gain falls to only -1/4 dB at 20
KHz.
BTW, Julius has updated 'filter.lib' to correct one of the coefficients of fdelay2
and added Thiran allpass interpolators
(
http://ccrma.stanford.edu/~jos/pasp/Thiran_Allpass_Interpolators.html) with very flat
frequency response.
5. There may be quality issues with some of the libs.
The 'bandfilter' for example behaves very strangely.
That's right, we should revise it. I must confess that until recently
most of our development efforts have been put on the compiler and quite
few on the libraries. But with a growing community things are changing...
Alternative main() for jack-gtk.
The code below solves two problems.
1. Auto-connection of ports requires a one-time user
action. Ports will be connected only if the environment
variables FAUST2JACK_INPUTS and FAUST2JACK_OUTPUTS are
defined, e.g. in ~/.bash_profile. They should be defined
as e.g.
export FAUST2JACK_INPUTS=system:capture_%d
export FAUST2JACK_OUTPUTS=system:playback_%d
Both are used as format strings in snprintf().
2. Use jack_client_open() instead of jack_client_new().
This will automatically generate a unique client name
in case there is a conflict. Creation of the UI is
delayed until the JACK connection is established, so
the correct name will also be shown in the window title.
Ciao,
--------------------------------------------------------------
int main(int argc, char *argv[] )
{
//gtk_init (&argc, &argv);
UI* interface;
jack_client_t* client;
char buf [256];
char rcfilename[256];
jack_status_t jackstat;
char *home;
char *pname;
char *jname;
jname = basename (argv [0]);
client = jack_client_open (jname, (jack_options_t) 0,
&jackstat);
if (client == 0) {
fprintf (stderr, "Can't connect to JACK, is the server
running ?\n");
exit (1);
}
if (jackstat & JackNameNotUnique) {
jname = jack_get_client_name (client);
}
jack_set_process_callback(client, process, 0);
jack_set_sample_rate_callback(client, srate, 0);
jack_on_shutdown(client, jack_shutdown, 0);
gNumInChans = DSP.getNumInputs();
gNumOutChans = DSP.getNumOutputs();
for (int i = 0; i < gNumInChans; i++) {
snprintf(buf, 256, "in_%d", i);
input_ports[i] = jack_port_register(client, buf,
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
}
for (int i = 0; i < gNumOutChans; i++) {
snprintf(buf, 256, "out_%d", i);
output_ports[i] = jack_port_register(client, buf,
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
}
interface = new GTKUI (jname, &argc, &argv);
DSP.init(jack_get_sample_rate(client));
DSP.buildUserInterface(interface);
home = getenv ("HOME");
if (home == 0) home = ".";
snprintf(rcfilename, 256, "%s/.%src", home, jname);
interface->recallState(rcfilename);
if (jack_activate(client)) {
fprintf(stderr, "Can't activate JACK client\n");
return 1;
}
pname = getenv("FAUST2JACK_INPUTS");
if (pname && *pname) {
for (int i = 0; i < gNumInChans; i++) {
snprintf(buf, 256, pname, i + 1);
jack_connect(client, buf,
jack_port_name(input_ports[i]));
}
}
pname = getenv("FAUST2JACK_OUTPUTS");
if (pname && *pname) {
for (int i = 0; i < gNumOutChans; i++) {
snprintf(buf, 256, pname, i + 1);
jack_connect(client, jack_port_name(output_ports[i]),
buf);
}
}
interface->run();
jack_deactivate(client);
for (int i = 0; i < gNumInChans; i++) {
jack_port_unregister(client, input_ports[i]);
}
for (int i = 0; i < gNumOutChans; i++) {
jack_port_unregister(client, output_ports[i]);
}
jack_client_close(client);
interface->saveState(rcfilename);
return 0;
}
--------------------------------------------------------------
Thanks again !
Cheers
Yann