grame-cncm / faust

Functional programming language for signal processing and sound synthesis
http://faust.grame.fr
Other
2.58k stars 322 forks source link

Unable to identify inputs #385

Closed sadko4u closed 4 years ago

sadko4u commented 4 years ago

Consider this code:

import("stdfaust.lib");

in_c = _;
in_b = _;
in_a = _;

process = +(in_a, in_b), _ : *(in_c);

The generated C++ code:

        virtual void compute (int count, FAUSTFLOAT** input, FAUSTFLOAT** output) {
                //zone1
                //zone2
                //zone2b
                //zone3
                FAUSTFLOAT* input0 = input[0];
                FAUSTFLOAT* input1 = input[1];
                FAUSTFLOAT* input2 = input[2];
                FAUSTFLOAT* output0 = output[0];
                //LoopGraphScalar
                for (int i=0; i<count; i++) {
                        output0[i] = (FAUSTFLOAT)((double)input2[i] * ((double)input0[i] + (double)input1[i]));
                }
        }

Seems that Faust provides no way to identify that 'in_c' is input2 but not input0. Is there any way to strictly define index of input in Faust code?

sletz commented 4 years ago

You can use the Faust Web IDE here to look at the black diagram.

(or open your exemple code are: https://faust.grame.fr/ide/index.html?autorun=1&voices=0&name=untitled1&inline=aW1wb3J0KCJzdGRmYXVzdC5saWIiKTsKCmluX2MgPSBfOwppbl9iID0gXzsKaW5fYSA9IF87Cgpwcm9jZXNzID0gKyhpbl9hLCBpbl9iKSwgXyA6ICooaW5fYyk7)

Then first first (the top one) will the first input (= input0 in generated code and so on. So basically you'll have to think in term of input/output order in the block diagram you're describing, that will corresponds to inputs/outputs 0...N in the generated code.

sadko4u commented 4 years ago

That's not good if I use the generated code as embedded into other code or if I create series of DSP processors that have strictly identified functional inputs. I don't want to watch block diagrams. I want to create *.dsp file with set of inputs, automatically generate a C++-file and then pass the signals to respective inputs knowing their indices. The lack of such functionality is ridiculous reprectively to all things were done to develop and populate FAUST language.

sletz commented 4 years ago

Basically in Faust, you are describing a signal processor, as a function taking a set of input signals (conceptually as infinite streams of samples) and producing a set of output signals. For instance if you write:

process = +;

you are describing a block that takes 2 inputs, add them and produce one output. The 2 inputs will be "connected" in their respective order on left and right channel of a stream input audio card for instance. So basically you describe an explicit ordering. Then the way the actual C++ generated code is connected to the external world (audio driver and UI) is written in so-called "architecture files", see: https://github.com/grame-cncm/faust/blob/master-dev/documentation/faust-quick-reference.pdf and possibly https://hal.archives-ouvertes.fr/hal-02159003

What is the lacking functionality?

sadko4u commented 4 years ago

This is true only when I have two inputs. It is not true when I have set of processors with varying input functions. Consider some filter with varying F and Q parameters where F and Q are control signals which are non-trivial. And consider some another device which also has F and Q parameters which also affect it's behaviour. And now we have some outer device which routes IN, F and Q to some abstract device which can be a filter or some another device. The operation becomes impossible since we don't know which input is IN, which is F and which is Q.

sadko4u commented 4 years ago

In other words, I can map a parameter:

in_param = hslider("in_param", dfl, min, max, step);

But can not map input:

input0 = _ ; // Which actually input is this? 0, 1, 2, or something else?

It would be nice to have:

input0 = dspinput(0);

Or at least:

input0 = _0; // or _1 or _2, etc...

The same for outputs

sletz commented 4 years ago

// Get the 'i' output of a arbitrary block 'b' outputN(i, b) = b : ba.selector(i, outputs(b));

// Test block block = (1,2,3,4);

output0 = outputN(0,block); output1 = outputN(1,block); output2 = outputN(2,block); output3 = outputN(3,block);

process = output0,output2;

sadko4u commented 4 years ago

Seems to be working improperly:

import("stdfaust.lib");

inputN(i, b) = b: ba.selector(i, outputs(b));

input = ( _,_,_ ) ;

in_c = inputN(0, input);
in_b = inputN(1, input);
in_a = inputN(2, input);

process = (in_a, in_b : +), in_c : *;

Now it considers 9 inputs:

        virtual void compute (int count, FAUSTFLOAT** input, FAUSTFLOAT** output) {
                //zone1
                //zone2
                //zone2b
                //zone3
                FAUSTFLOAT* input0 = input[0];
                FAUSTFLOAT* input1 = input[1];
                FAUSTFLOAT* input2 = input[2];
                FAUSTFLOAT* input3 = input[3];
                FAUSTFLOAT* input4 = input[4];
                FAUSTFLOAT* input5 = input[5];
                FAUSTFLOAT* input6 = input[6];
                FAUSTFLOAT* input7 = input[7];
                FAUSTFLOAT* input8 = input[8];
                FAUSTFLOAT* output0 = output[0];
                //LoopGraphScalar
                for (int i=0; i<count; i++) {
                        output0[i] = (FAUSTFLOAT)((double)input6[i] * ((double)input2[i] + (double)input4[i]));
                }
        }
sletz commented 4 years ago

OK, so why not using this approach:

foo1(in_a, in_b, in_c) = (in_a, in_b : +), in_c : *;
foo2(in_a, in_b, in_c) = (in_c, in_b : +), in_a : *;
foo3(in_a, in_b, in_c) = (in_c, in_c : +), in_c : *;
foo4 = \(in_a, in_b, in_c).((in_c, in_b : +), in_a : *); 

//process = foo1;
//process = foo2;
//process = foo3;
process = foo4;

where you name inputs are arguments of a function and use them where you need in the function body ?

sadko4u commented 4 years ago

This looks out like a serious code duplication. Also, what should I do if I want to inject one of the inputs directly into the middle of the processing chain? Rewrite the previous part of the chain to add this argument? Rewrite all affected functions to pass this parameter as an Nth argument? That's not a good idea.

orlarey commented 4 years ago

Sorry but this is not a forum to learn how to program in Faust, but to report bugs. Please read the tutorials and documentation available on https://faust.grame.fr.

sadko4u commented 4 years ago

@orlarey So, you have no good solution now and just closing the issue? Seriously?

sletz commented 4 years ago

For programming questions, better use the mailings lists https://faust.grame.fr/community/mailing-lists/ or possibly the Faust Slack channel: https://join.slack.com/t/faustaudio/shared_invite/enQtNzk2ODMxMzM4MzExLTdjYmUyZmQ4MzRkMWY5OTFhNTIzYmMwMDQ2YmM5M2VlYjU1YjBlYjg4OTQxM2RjMGM5Njc0NjVmZGJjY2E3MDI

josmithiii commented 4 years ago

My usual approach to naming signals goes like this:

inSigs = , ; leftChannel = inSigs : , !; rightChannel = inSigs : !, ; process = leftChannel, rightChannel;

Does this apply for you? (Buzzed through email history very quickly)

On Thu, Jan 16, 2020 at 1:55 AM Stéphane Letz notifications@github.com wrote:

For programming questions, better use the mailings lists https://faust.grame.fr/community/mailing-lists/ or possibly the Faust Slack channel: https://join.slack.com/t/faustaudio/shared_invite/enQtNzk2ODMxMzM4MzExLTdjYmUyZmQ4MzRkMWY5OTFhNTIzYmMwMDQ2YmM5M2VlYjU1YjBlYjg4OTQxM2RjMGM5Njc0NjVmZGJjY2E3MDI

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/grame-cncm/faust/issues/385?email_source=notifications&email_token=AAQZKFNW5WJHN33MQT3VYVLQ6AVHFA5CNFSM4KG2PYYKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJDOY3Q#issuecomment-575073390, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAQZKFJKDOV5DGSUIPO4XKDQ6AVHFANCNFSM4KG2PYYA .

--

Julius O. Smith III jos@ccrma.stanford.edu Professor of Music and, by courtesy, Electrical Engineering CCRMA, Stanford University http://ccrma.stanford.edu/~jos/

sletz commented 4 years ago

This end up with a block with 4 inputs and 2 outputs, which is not what was wanted AFAICS:

virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
        FAUSTFLOAT* input0 = inputs[0];
        FAUSTFLOAT* input1 = inputs[1];
        FAUSTFLOAT* input2 = inputs[2];
        FAUSTFLOAT* input3 = inputs[3];
        FAUSTFLOAT* output0 = outputs[0];
        FAUSTFLOAT* output1 = outputs[1];
        for (int i = 0; (i < count); i = (i + 1)) {
            output0[i] = FAUSTFLOAT(float(input0[i]));
            output1[i] = FAUSTFLOAT(float(input3[i]));
        }
    }
sadko4u commented 4 years ago

@sletz , right. I mean if the language follows some kind of digital schematics, then at least this should be possible: image

sletz commented 4 years ago

Better use the Faust Slack channel (faustaudio.slack.com).