Faust with MIDI and Knobs

Hi,

I’m trying a few lines of Faust on the Magus but I’ve run into problems. There is no documentation as far as I can find so I have to guess. My problem is that if I compile my faust code with MIDI support (first line):

declare options "[midi:on]";
import("stdfaust.lib");
decimalpart(x) = x-int(x);
phase(f) = f/ma.SR : (+ : decimalpart) ~ _ ;
timbre(f) = phase(f)*0.5 + phase(f*2)*0.25 + phase(f*3)*0.125;

process = timbre(hslider("freq[OWL:A]", 440, 20, 10000, 1)) 
* hslider("gain[OWL:B]", 0.5, 0, 1, 0.01) 
* (button("gate") : en.adsr(0.1,0.1,0.98,0.1));

effect = dm.zita_light;

MIDI works fine from my USB-midi keyboard. But the parameters are called Parameter A etc. and have no function. If I on the other hand leave out the first line. I get the parameter text freq and gain, but obviously no MIDI support.

Is there a way to get both MIDI and Parameter knobs?

I don’t know the state of MIDI support in the OWL architecture that is developed here?

But anyway: MIDI support is done by adding specific metadata in UI items like [midi:ctrl 7] for instance. Read the documentation here: MIDI Support - Faust Documentation.

An extensive DSP test case is available here: faust/midi_tester.dsp at master-dev · grame-cncm/faust · GitHub.

@sletz you’ve misunderstood his question, the question is not about CC binding to parameters (which is not available in Owl arch template yet, btw).

The issue is that when the special variables are used (freq/gain/gate), they are set based on MIDI notes. This means that they no longer can be managed by Owl as parameter and read from input. So they are excluded from Owl’s input parameters that are read from hardware and only use value set by MIDI.

So no, there’s no way to do it like that and it’s expected to work like that at least with current code. But you can decide which should take precedence (MIDI or Owl input) and do something like this:

freq_input = hslider("Freq IN[OWL:A]", 440, 20, 10000, 1);
freq_midi   = hslider("freq", 440, 20, 10000, 1);
freq = ba.if(freq_input > 0, freq_input, freq_midi);

In other words, if you want to use MIDI and non-MIDI input, you have to handle it yourself explicitly.

1 Like

OK I was not aware of all these OWL architecture specific issues.

Thank you both for your answers! @antisvin having both connections worked great. Feels like I got a bit closer to understanding the magic that can happen with very little Faust code :slight_smile:

May I take the opportunity to ask a very related question. Can I “tap out” to the outputs of the Magus/OWL with some Faust function like I tap in above? I’m thinking of the default groovebox patch having LFOs going out of certain ports.

@eblade, yes you can use parameter outputs from Faust. I’ve uploaded docs that I was preparing that has an example for this and other features, so have a look at https://github.com/antisvin/OpenWareLab/blob/faust-docs/Faust/Faust.md#parameter-output . Note that some of the stuff mentioned there is not available in official branch yet (that would include VOct handling and longer list of available parameter names).

Using outputs is a bit awkward because both Faust and OpenWare have some quirks. For Faust you must use an appropriate widget (hbargraph or vbargraph) and you have to use attach primitive. And OpenWare requires to use “>” in the end of the label string, the same way is you have to do in C++ code.

Nice! More documentation is definitely welcome!

Tried the output example and found that you seem to have the wrong number of parameters to hbargraph(). According to https://faust.grame.fr/doc/manual/index.html#hbargraph-primitive it would only take label, min and max. So, I went with this:

import("stdfaust.lib");

freq     = hslider("Frequency[OWL:A]", 60, 60, 440, 1);
lfo_freq = hslider("LFO frequency[OWL:B]", 1, 1, 440, 1);
lfo_out  = hbargraph("LFO>[OWL:C]", -1, 1);

process = attach(os.osc(freq), os.osc(lfo_freq) : lfo_out);

I can get the the sine wave on out left to have it’s frequency modified by the LFO. However, it sounds kind of weird. When looking at in an oscilloscope I see that the output signal looks nothing like a sine wave, it is very distorted. Do I need 20.6 beta for this to work or is this a sampling speed limitation?

Also, is this the way to make it stereo (when listening with headphones this can be nice):

wave = attach(os.osc(freq), os.osc(lfo_freq) : lfo_out);
process = wave, wave;

In the web editor for Faust it seem to do this automatically but on the Magus this does the trick.

Thanks!

Sorry for confusion. You’re right about bargraph parameters - I have fixed this in patch sources, but haven’t updated docs with corrected code yet. Was planning to do it before publishing, but ended up uploading docs as is.

As for LFO itself, I think you should lower its frequency. I’ve used this in my updated version:

lfo_freq = hslider("LFO frequency[OWL:B]", 0.3, 0.01, 1.0, 0.01);

You can’t just use CV inputs/outputs for audio rate data. It doesn’t process data for every sample, but once per audio block (every 64 samples). So you’re effectively using sampling rate of 750Hz in case of CV (48000 / 64). This is still good enough for slow LFOs/envelopes/gates, but if you want to hear harmonics higher than 375 Hz you need to use audio outputs for that.

With shorter block size you’ll be able to process CV with higher sampling rate. Martin mentioned that he’ll be adding audio block size changing to firmware eventually (or I may even give it a try myself).

Also, it’s probably a good idea to use smoothing for data that is received from CV inputs using something like this (you may want to experiment with smoothing value):

lfo_freq = hslider("LFO frequency[OWL:B]", 0.3, 0.01, 1.0, 0.01) : si.smooth(0.99);

Regarding stereo signals - I prefer to write it like this:

process = wave <: _, _;

In this case your version is equivalent and perfectly valid, but this way code can be more explicit about signal flow. Also, I suspect that writing it the way you did may cause double calculation in one particular case - if it would be using a foreign function which doesn’t takes parameters (i.e. if we used a function written in C that generates random number).