Discussion:
[Faudiostream-devel] [LAD] Prototyping algorithms and ideas
Yann Orlarey
2008-01-29 22:36:30 UTC
Permalink
Hi Fons,

Here is a quick solution using Faust :

import("filter.lib");

line(i) = vgroup("line %i", *(g) : fdelay2(1024, d))
with { g = vslider("gain (dB)", -60, -60, 4, 0.1) : db2linear :
smooth(0.995);
d = nentry("delay (samp)", 0, 0, 1000, 0.1) :
smooth(0.995); };

process = hgroup("", par(i, 10, line(i)) );

The first line imports a library of filters (written in Faust by Julius
Smith) that includes several fractional delays based on Lagrange
interpolation.

Then we define a 'line' that combines in sequence (':' operator) a gain
and a fractional delay encapsulated in a vertical layout. Here we use
fdelay2 a fractional delay with a second order Lagrange interpolation.
It can be replaced by fdelay3 or fdelay4 if better interpolations are
needed.

The gain value g is defined by a vertical slider (in dB) which is
converted in a linear value and then filtered to avoid clicks during
rapid movements. The delay value d is defined by a numerical entry box
also filtered to avoid clicks. The parameter m is the maximum size of
the delay and should be a power of 2.

The last line defines process, the equivalent of main in C, as a
parallel composition of 10 lines.

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%.

Cheers

Yann
Also, nice in the fact that you can do per-sample computations
easily,
How ? I seem to have missed something...
Because you can wait in a 1-sample loop?
Yes, it will use your whole CPU for a loop like that, but this thread
is about prototyping. Obviously you'd rewrite in C for a real
application.
I never claimed chuck is perfect, but I've been liking it a lot
lately. Sure, it can have performance issues depending on how you
use
it, but the nice thing is having the option to abuse it that way when
necessary.
This is one of the things I wanted to create a 'rapid prototype'
- a delay line,
- allowing high-quality fractional-sample delays,
- at least 12 outputs, for each two controls: delay, gain,
- smooth 'crossfading' between two control sets, both delay
and gain, controlled by a GUI or by OSC.
It should not take more than 20% CPU on a 2G P4
(other things have to run at the same time).
If you know how to get this faster than by actually
writing it in C++, please let me know !
Fons Adriaensen
2008-01-30 14:08:31 UTC
Permalink
Post by Yann Orlarey
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
I think they call that "game, set and match".
Not really, but still I'm impressed :-)

- Even fdelay4 is not up to the task. There are
gain variations of more than 6dB for a 20 kHz
signal, Fs = 48 kHz. Even stranger, they are
not symmetric w.r.t. the fraction of the delay,
e.g. 10.3 samples shows a different gain than
9.7. Which makes me suspicious.

- My usual grunge: unless you want me accidentally
destroy some very expensive equipment which is not
even mine, the generated JACK apps

MUST NOT AUTOCONNECT --- NEVER --- TO ANYTHING.

- The apps also autosave their state to $HOME/.***rc.
Any way to disable this ?

Ciao,
--
FA

Laboratorio di Acustica ed Elettroacustica
Parma, Italia

Lascia la spina, cogli la rosa.
Stéphane Letz
2008-02-01 15:36:45 UTC
Permalink
Post by Fons Adriaensen
- The apps also autosave their state to $HOME/.***rc.
Any way to disable this ?
If you want to change that kind behaviour for now,
...
One man's 'kind behaviour' is another one's bug.
Just received a mail from on of the people (not
a programmer - a _real_ _user_) I work with here
and who has started using Faust.
He wrote he had discovered a bug: default
values given to sliders etc. only work the
first time a program is run. And he's right....
What do you mean by "first time"? In the jack-gtk wrapper? related to
the jack-gtk save/restore state managment?

Thanks

Stephane
Stéphane Letz
2008-02-01 16:28:46 UTC
Permalink
Post by Stéphane Letz
He wrote he had discovered a bug: default
values given to sliders etc. only work the
first time a program is run. And he's right....
What do you mean by "first time"? In the jack-gtk wrapper? related
to the
jack-gtk save/restore state managment?
The first time you run a new program made with jack-gtk,
the UI elements are preset to the default values defined
in your Faust code. The next time it reads the saved
state file, making the defaults useless.
Since there's nothing in the Faust language about
persistence of GUI values, but it _has_ defaults for
them, that behaviour *is* a bug...
Well it is a "non Fons approved" behaviour ((-:

I guess *some* users like this save/restore behaviour. Maybe a new "-
default" parameter that would not use the saved state could be added.
Patch welcomed...

Stephane
Fons Adriaensen
2008-02-01 17:07:55 UTC
Permalink
And I'm not alone.

The Faust language lets the programmer specify initial
values, and it does not say that they will be derived
from the environment. So they should be respected.
Just add a File menu with Load / Save. Or just two
buttons...
--
FA

Laboratorio di Acustica ed Elettroacustica
Parma, Italia

Lascia la spina, cogli la rosa.
Albert Graef
2008-02-01 18:19:03 UTC
Permalink
The first time you run a new program made with jack-gtk,
the UI elements are preset to the default values defined
in your Faust code. The next time it reads the saved
state file, making the defaults useless.
I must admit that I was also surprised by this behaviour the first time
I ran Faust's jack-gtk programs. Of course I immediately suspected that
there might be some gtk config magic at work there, but as most of the
other archs obey the init values in the Faust source, it shouldn't come
as a surprise that this might be confusing to new Faust users.

I understand that this behaviour can be convenient at times, but I
really think that it should be an option, turned off by default.

While everybody starts picking his nits with Faust (I actually think
that this is good, after all that's how open source projects improve and
stay alive!), here are my two pet peeves. Actually, they're not only my
own, but I also noticed that my students often have difficulties with
them. I think I complained on the list about these before, so please
bear with me. :) As you all know, I'm a really big fan of Faust and have
contributed to it myself, but these two things keep irritating me.

1. No real unary minus operator.

You can write, say, -1*x or -1, but not -x. I understand that changing
this introduces conflicts with the current application syntax, where
-(x) is a partial application (equivalent to \(y).(y-x)) rather than
unary minus. The solution in other FPLs with curried applications, like
Haskell or Q, is to give unary minus special treatment and to *always*
interpret a prefix - as unary minus. Of course, then you'd have to write
something like +(-x) (which would then be legal Faust syntax) for the
few occassions where you really want \(y).(y-x).

I'd say that this is quite a reasonable tradeoff -- make an ubiquitous
feature work at the expense of an obscure feature (and in fact I think
that writing +(-x) instead of -(x) isn't a big deal anyway). And it's
really a minor change in the syntax, so it shouldn't be too hard to
implement.

2. "Late" bindings.

I just ran across this again in my current Faust course. Consider the
following code fragment from a chorus effect (from the top of my head;
this was actually an exercise given to the students):

import("music.lib");
delay = hslider("delay", ...);
depth = hslider(...); freq = hslider(...); level = hslider(...);
process(x) = x+level*fdelay(1<<16, (1+depth*osci(freq))*delay, x);

Well, the issue is that there's a delay function in music.lib, in terms
of which fdelay (also in music.lib) is defined, and a different
definition of delay in the main module. With lexical binding of
identifiers, there shouldn't be a problem, because music.fdelay should
still use music.delay and not my definition of delay. Right? Wrong, the
Faust compiler substitutes my own definition of delay into fdelay, with
predictable if somewhat undesirable results...

Virtually *all* students in the course were bitten by this (I'm talking
about 6th semester cs students with some knowledge of FP/Scheme here, so
they are not exactly noobs any more). I'm not sure whether it's actually
a lexical-vs-dynamic binding issue, but I really think that this ought
to be changed.

Both items call for changes which affect backward-compatibility. But
better do it now before Faust hits 1.0, no? Maybe the Faust compiler
could optionally warn about occurrences of these, so that it would be
easy to migrate existing Faust source.

Comments?

Cheers,
Albert
--
Dr. Albert Gr"af
Dept. of Music-Informatics, University of Mainz, Germany
Email: ***@t-online.de, ***@muwiinfa.geschichte.uni-mainz.de
WWW: http://www.musikinformatik.uni-mainz.de/ag
Fons Adriaensen
2008-02-04 13:50:26 UTC
Permalink
Post by Albert Graef
While everybody starts picking his nits with Faust (I actually think
that this is good, after all that's how open source projects improve and
stay alive!), here are my two pet peeves....
After a WE of playing with it (which makes me a very
novice and inexperienced user, these are my remarks.
It's very well possible I missed something, so please
correct if there's anything that plain wrong...

1. Mixing of DSP and GUI code.

The only way to have a control parameter is to associate
it with a GUI widget. This may be convenient for generating
small demo / test programs, but it doesn't make much sense
otherwise. If the generated code becomes part of a larger
system it doesn't know where its control parameters come
from, nor should it care.

The specification of a simple prototyping GUI could be
part a development environment that uses Faust, but it
should IMHO not be part of the language itself - it just
obscures the DSP code.

2. No support for explicit control rate.

The language itself doens't seem to have any concept
of control rate - this is introduced only by the form
the generated C++ code takes: a loop processing a
number of samples, and implicitly by the GUI widgets.

Calculations depending on the control values that
would produce a constant result within the loop are
optimized out, which is nice. But it's AFAICS not
possible to do this:

f = control parameter
_f = stored value (class data member) of the same
N = number of samples to process in current call

process (N, ...)
{
f = get_control (...);
df = (f - _f) / N;

while (N--)
{
// process sample using _f
_f += df;
}
}

In plain words, the control parameter is interpolated
linearly and reaches the requested value exactly at
the first sample past the current period.

This is an idiom I use in almost all LADSPA plugins.
It makes a lot of sense in any system that supports
a well-defined control rate, as it exactly defines
the time relation between sample rate and control
rate signals, while allowing for the latter to be
interpolated.

But it seems impossible to do this in Faust - you
can't create _f, and you don't know N.

It's also just the simplest case.

- The calculations performed before the loop could
be much more complex, and also be conditional on
combinations of control values, their history,
and current state.

- You may want to share the results of the control
rate calculation for a number of processing loop
instances (known at execution time only), e.g.
for plugins that are defined as single channel
and replicated by the host for multi-channel
operation.

- You may also want to perform some control rate
calculations after the sample processing loop.

One way to make this possible would be to have
_two_ Faust code blocks, one for control rate
and one for sample rate, both using the full
language. The sample rate part would be 'called'
somehow from within the control rate code.

Now 'called' is a of course an 'imperative' term.
but it should be possible to express this in a
functional way, by seeing the sample rate part
as a producer of control rate values (this may
require a dummy one to create the dependency),
Then for the control rate code the sample rate
processing is just an invisible side effect.

This layering could be repeated: for example
the control rate code could become a subroutine
of an even slower control rate level, or the
sammple rate code could call subroutines that
operate at a higher rate (now I'm dreaming).
--
FA

Laboratorio di Acustica ed Elettroacustica
Parma, Italia

Lascia la spina, cogli la rosa.
Yann Orlarey
2008-02-04 23:03:22 UTC
Permalink
Sorry for not being more responsive, but it is a busy period and
unfortunately my latency is going to be quite high in the next weeks...
Post by Fons Adriaensen
1. Mixing of DSP and GUI code.
The only way to have a control parameter is to associate
it with a GUI widget. This may be convenient for generating
small demo / test programs, but it doesn't make much sense
otherwise. If the generated code becomes part of a larger
system it doesn't know where its control parameters come
from, nor should it care.
Conceptually Faust uses a synchronous mono-rate model. It makes no
distinction between audio signals and control signals. This is why you
can freely mix control signals and audio signals in a same expression.
The distinction only appears at code generation. The compiler
automatically optimizes expressions involving only constants or
constants and control signals.

The association between control parameters and GUI widgets is done by
the "architecture" file. Architecture files are free to ignore these
indications on how to reflect the control parameters in the user
interface. For example max-msp, and vst architecture simply ignore the
fact that a control parameter is a slider or a button etc.
Post by Fons Adriaensen
The specification of a simple prototyping GUI could be
part a development environment that uses Faust, but it
should IMHO not be part of the language itself - it just
obscures the DSP code.
Faust provides an extremely concise and clear way to describe graphic
user interfaces. But you want, it is very easy to completely separate
the "algorithmic" part from the "user interface" part in your Faust code.
Post by Fons Adriaensen
2. No support for explicit control rate.
The language itself doens't seem to have any concept
of control rate - this is introduced only by the form
the generated C++ code takes: a loop processing a
number of samples, and implicitly by the GUI widgets.
Calculations depending on the control values that
would produce a constant result within the loop are
optimized out, which is nice. But it's AFAICS not
f = control parameter
_f = stored value (class data member) of the same
N = number of samples to process in current call
process (N, ...)
{
f = get_control (...);
df = (f - _f) / N;
while (N--)
{
// process sample using _f
_f += df;
}
}
In plain words, the control parameter is interpolated
linearly and reaches the requested value exactly at
the first sample past the current period.
Of course it is possible to do this kind of interpolation. Here is the
code :

interpolator(n,x) = interp ~ (_,_,_) : !,!,_
with {
interp (s,d,y) = (s+1)%n,
select2(s==0, d, (x-(y+d))/n),
y+d;
};


Where n is duration of the interpolation period in samples, and x the
signal to interpolate. To see it in action I have attached a picture.

Cheers

Yann
Fons Adriaensen
2008-02-05 12:12:16 UTC
Permalink
Post by Fons Adriaensen
f = control parameter
_f = stored value (class data member) of the same N = number of samples
to process in current call
process (N, ...)
{
f = get_control (...);
df = (f - _f) / N;
while (N--)
{
// process sample using _f
_f += df;
}
}
In plain words, the control parameter is interpolated
linearly and reaches the requested value exactly at
the first sample past the current period.
Of course it is possible to do this kind of interpolation. Here is the code
interpolator(n,x) = interp ~ (_,_,_) : !,!,_
with {
interp (s,d,y) = (s+1)%n,
select2(s==0, d, (x-(y+d))/n),
y+d;
};
Where n is duration of the interpolation period in samples, and x the
signal to interpolate. To see it in action I have attached a picture.
But how do you make n correspond to the first argument to compute()
(which need not even be the same for each call) ?
--
FA

Laboratorio di Acustica ed Elettroacustica
Parma, Italia

Lascia la spina, cogli la rosa.
Orlarey Yann
2008-02-05 17:35:28 UTC
Permalink
Post by Fons Adriaensen
Of course it is possible to do this kind of interpolation. Here is the code
interpolator(n,x) = interp ~ (_,_,_) : !,!,_
with {
interp (s,d,y) = (s+1)%n,
select2(s==0, d, (x-(y+d))/n),
y+d;
};
Where n is duration of the interpolation period in samples, and x the
signal to interpolate. To see it in action I have attached a picture.
But how do you make n correspond to the first argument to compute()
(which need not even be the same for each call) ?
Good question ;-)

Faust has a foreign function mechanism allowing to declare external C
functions and constants (see 'math.lib' for example). For example the
sampling rate 'SR' is defined as :

SR = fconstant(int fSamplingFreq, <math.h>);

I have added the possibility to also declare foreign variables. The CVS
version of 'math.lib' now contains the definition of 'BS' (block size) :

BS = fvariable(int count, <math.h>);

which makes the link with 'count', the first argument of compute().

Therefore 'interpolator(BS)' should provide the expected behavior...

Cheers

Yann

Fons Adriaensen
2008-01-31 00:21:07 UTC
Permalink
Hi Yann,
Post by Yann Orlarey
...
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
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.

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.

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.

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.

5. There may be quality issues with some of the libs.
The 'bandfilter' for example behaves very strangely.


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;
}


--------------------------------------------------------------
--
FA

Laboratorio di Acustica ed Elettroacustica
Parma, Italia

Lascia la spina, cogli la rosa.
Yann Orlarey
2008-01-31 17:29:58 UTC
Permalink
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.
Post by Fons Adriaensen
Hi Yann,
Post by Yann Orlarey
...
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
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.
Post by Fons Adriaensen
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...
Post by Fons Adriaensen
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.
Post by Fons Adriaensen
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.
Post by Fons Adriaensen
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...
Post by Fons Adriaensen
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
Albert Graef
2008-02-01 04:02:13 UTC
Permalink
Yann Orlarey wrote:
[denormals issues]
Post by Yann Orlarey
This is a real problem but it should be solved on modern intel cpu by
enabling the FTZ mode.
It would be useful if the Faust guide had a brief explanation of
denormals, along with instructions on how to enable FTZ with gcc and the
Intel compiler, and other workarounds. Or maybe it's time to start a
Faust FAQ? This problem is surely going to bite a lot of dsp newbies who
start playing with Faust on the "wrong" system.

Albert
--
Dr. Albert Gr"af
Dept. of Music-Informatics, University of Mainz, Germany
Email: ***@t-online.de, ***@muwiinfa.geschichte.uni-mainz.de
WWW: http://www.musikinformatik.uni-mainz.de/ag
Yann Orlarey
2008-02-01 07:14:39 UTC
Permalink
Post by Albert Graef
[denormals issues]
Post by Yann Orlarey
This is a real problem but it should be solved on modern intel cpu by
enabling the FTZ mode.
It would be useful if the Faust guide had a brief explanation of
denormals, along with instructions on how to enable FTZ with gcc and the
Intel compiler, and other workarounds. Or maybe it's time to start a
Faust FAQ? This problem is surely going to bite a lot of dsp newbies who
start playing with Faust on the "wrong" system.
Albert
Right. The interactions with the various gcc flags are not very easy to
understand. The FTZ mode is supposed to be enabled by the -ffast-math
flag. But alone this flag is not enough.

Here are various tests I did on karplus32.dsp to figure out the right
combination of gcc flags to avoid denormals.

The first thing is to modify Karplus32 to produce denormals by
commenting out " :+(leak)" line 21 :
trigger(n) = upfront : release(n) : >(0.0); // : +(leak);

Then compile karplus32.dsp only with "-O3". If you play with the
modified karplus32, after few seconds (~30s) of inactivity it starts
using 100% of the CPU.

Now if you recompile karplus32.dsp using "-O3 -ffast-math" you will
still have denormals because you need to be in sse mode. Being in sse
mode "-O3 -march=native -mfpmath=sse" is not enough and still produces
denormals.

The minimal combination to activate FTZ mode seems to be :

"-O3 -march=native -mfpmath=sse -ffast-math"

when you are on a sse capable cpu. Here it works, you don't have
denormals any more :-).

Yann

PS : from the gcc manual about -ffast-math : "This option should never
be turned on by any ‘-O’ option since it can result in incorrect output
for programs which depend on an exact implementation of IEEE or ISO
rules/specifications for math functions."
Tim Blechmann
2008-02-01 09:47:50 UTC
Permalink
Post by Yann Orlarey
Post by Albert Graef
[denormals issues]
Post by Yann Orlarey
This is a real problem but it should be solved on modern intel cpu by
enabling the FTZ mode.
It would be useful if the Faust guide had a brief explanation of
denormals, along with instructions on how to enable FTZ with gcc and the
Intel compiler, and other workarounds. Or maybe it's time to start a
Faust FAQ? This problem is surely going to bite a lot of dsp newbies who
start playing with Faust on the "wrong" system.
Albert
Right. The interactions with the various gcc flags are not very easy to
understand. The FTZ mode is supposed to be enabled by the -ffast-math
flag. But alone this flag is not enough.
the FTZ/DAZ flags of the sse unit can also be set from the application
itself ... something like:

#ifdef __SSE2__
#define _MM_DENORM_ZERO_ON 0x0040
#include <xmmintrin.h>
#endif

int main()
{
#ifdef __SSE2__
_mm_setcsr(_MM_FLUSH_ZERO_ON | _MM_MASK_UNDERFLOW | _mm_getcsr());
_mm_setcsr(_MM_DENORM_ZERO_ON | _mm_getcsr());
#endif
}

best, tim

--
***@klingt.org
http://tim.klingt.org

A paranoid is a man who knows a little of what's going on.
William S. Burroughs
Yann Orlarey
2008-02-01 10:15:32 UTC
Permalink
Post by Tim Blechmann
the FTZ/DAZ flags of the sse unit can also be set from the application
#ifdef __SSE2__
#define _MM_DENORM_ZERO_ON 0x0040
#include <xmmintrin.h>
#endif
int main()
{
#ifdef __SSE2__
_mm_setcsr(_MM_FLUSH_ZERO_ON | _MM_MASK_UNDERFLOW | _mm_getcsr());
_mm_setcsr(_MM_DENORM_ZERO_ON | _mm_getcsr());
#endif
}
best, tim
Thanks Tim, that's useful in particular when the -ffast-math
optimisations are problematic....

Yann
Albert Graef
2008-02-01 11:21:45 UTC
Permalink
Can that code be generated by Faust's C++ backend (controlled by a
compiler option maybe, as it changes the program semantics), or do we
have to change all architecture files accordingly?
Post by Yann Orlarey
Post by Tim Blechmann
the FTZ/DAZ flags of the sse unit can also be set from the application
#ifdef __SSE2__
#define _MM_DENORM_ZERO_ON 0x0040
#include <xmmintrin.h>
#endif
int main()
{
#ifdef __SSE2__
_mm_setcsr(_MM_FLUSH_ZERO_ON | _MM_MASK_UNDERFLOW | _mm_getcsr());
_mm_setcsr(_MM_DENORM_ZERO_ON | _mm_getcsr());
#endif
}
best, tim
Thanks Tim, that's useful in particular when the -ffast-math
optimisations are problematic....
--
Dr. Albert Gr"af
Dept. of Music-Informatics, University of Mainz, Germany
Email: ***@t-online.de, ***@muwiinfa.geschichte.uni-mainz.de
WWW: http://www.musikinformatik.uni-mainz.de/ag
Tim Goetze
2008-02-01 16:01:35 UTC
Permalink
[Paul Davis]
Post by Yann Orlarey
"-O3 -march=native -mfpmath=sse -ffast-math"
when you are on a sse capable cpu. Here it works, you don't have
denormals any more :-).
Ardour takes manual control of FTZ and DAZ flags, and also offers DC
offsets (adding a very very tiny value to every sample) as ways to
control denormals. Our experience (or our users' experience) has been
that FTZ is better than DAZ, but that neither reduces denormal effects
as much as a DC offset. It is still possible to see denormal-induced
performance slowdowns even when FTZ is in effect, although the
magnitude
is reduced.
On this Core2 (6600) chip, this:

#ifdef __SSE3__
_MM_SET_DENORMALS_ZERO_MODE (_MM_DENORMALS_ZERO_ON);
#endif

#ifdef __SSE__
_MM_SET_FLUSH_ZERO_MODE (_MM_FLUSH_ZERO_ON);
#endif

along with -mfpmath=sse -msse -msse3 reliably eliminates denormals
completely. DSP code runs at full speed with no more need for DC,
noise, Nyquist or other signal injection.

Flush-to-zero alone did not bring a palpable improvement when I
evaluated the different options. On this chip that is, of course.

Cheers, Tim
Loading...