electro-smith / libDaisy

Hardware Library for the Daisy Audio Platform
https://www.electro-smith.com/daisy
MIT License
331 stars 139 forks source link

Create initial faust2daisy utility #45

Closed PaulBatchelor closed 4 years ago

PaulBatchelor commented 4 years ago

General Idea

In the spirit of the faust conversion applications I'm thinking this should be a all-in-one sort of deal. Running faust foo.dsp generates foo.bin, which can then be flashed and run on the daisy board. No pre-req makefile or boilerplate code should be needed. All of that should generated or managed inside of the faust2daisy tool. Users should be able to easily interchange CV, knobs, gates, and buttons together in FAUST code.

Potential Action Plan

The following outlines how I think we can approach this right now. This is only a starting point. It's likely that new discoveries and issues things will change things a bit.

Phase 1: architecture file

The first step to making any faust code generator tool is to build a faust architecture file. An architecture file is a special text template file read by the FAUST preprocesser. During code generation, FAUST will read special macros and symbols and inject DSP code into them. FAUST has an interface for handling DSP change (setup/cleanup, connecting parameters, rendering a block of audio, etc).

FAUST has several output language targets now, including C99 C. While the default and best supported version is C++, I'm going to be way more comfortable using C (I also just know I'm going to do a shit job if I try to pretend to be a C++ programmer). It works quite well at this point, and I've already created a handful of FAUST C architecture files for projects like Soundpipe + Patchwerk, so I know my way around them.

The process of building an is connecting their way of doing things into your way of doing things. It will be an incremental process.

The easiest thing to do first would be to retrofit a single example file and retrofit it to generate a stereo signal (two sine waves of different frequences, for instance). Then, we can get input signals working. For now, we will only focus on just one stereo in/out combination using the internal audio codec.

One issue I already forsee happening is the fact that FAUST dsp routines take in each channel of audio as a separate block, rather than being interleaved. It appears we have some control over this, so it looks possible to set up a non-interleaved mode.

UI control is going to be an interesting one. They have a really specific way of handling parameters. FAUST auto-generates UI glue code, and you more or less have to play ball with that structure. Faust parameters are control rate, not audio rate. The Daisy has a finite number of physical peripherals, and this fact actually simplifies things quite a bit. The FAUST UI glues works by supplying a floating point pointer than an external application can read/write to. It CANNOT seem to work the other way around (you can't give it a pointer that it can read from). In the past, this detail has made things more difficult for me, but it's not an impossible solution to solve.

Step 2: FAUST helper code

Since there are fixed inputs/outputs, I think it would be useful to build some abstractions in FAUST. The main thing I'm thinking about is getting knobs + button abstractions built. If we give them specific labels, like "dsy_knob_1", it will make the code generation process way simpler.

For this initial attempt, I'm okay with just knobs, and maybe CV inputs. Buttons/gates can wait unless they are super easy.

Step 3: faust2daisy script

The faust2daisy utility is just a shell script, but a reasonably robust one. We would ideally want to make it work out of the box on all platforms. The important thing here is that the user does not have to deal with any extra files, only the DSP file. This may mean we "hide" the core files elsewhere (there is precedent for this already in FAUST).

PaulBatchelor commented 4 years ago

@stephenhensley Assigning you here with me only because I may need help with the non-interleaved mode bit.

stephenhensley commented 4 years ago

This sounds great. Thanks for all the detail. Faust has been pretty foreign to me, so this is terrific.

Re. Step 2 and control abstractions, I'd almost rather have fixed "Parameters" or something like that that could be assigned to controls in daisy. This would be a bit more flexible with different hardware (for example knob 1 might be knob 3 on a different PCB, and there are already about 4 daisy sub-board products in development). So flexibility is pretty important.

De/re-interleaving the audio shouldn't really be an issue. worst-case it takes a BLOCKSIZE loop on each side to prep the audio for the hardware. I'm not sure if the codec on the seed supports a non-interleaved format, but that won't be a problem in software.

PaulBatchelor commented 4 years ago

Re. Step 2 and control abstractions, I'd almost rather have fixed "Parameters" or something like that that could be assigned to controls in daisy.

That's sort of what I had in mind. Something like this:

import("stdfaust.lib");

dsy_knob1 = hslider("dsy_knob1", 0.5, 0, 1, 0.0001);

freq_knob = dsy_knob1;

process = os.osc(10 + 20000 * freq_knob);

Here, "dsy_knob1" is just a variable representing the UI control, and can be treated like any other signal in FAUST. In this example, I'm assigning it to the variable with the more context-aware name freq_knob1 just so the code is more readable.

In practice, a variable like "dsy_knob1" would be abstracted away.

This would be a bit more flexible with different hardware (for example knob 1 might be knob 3 on a different PCB, and there are already about 4 daisy sub-board products in development). So flexibility is pretty important.

As I was alluding to in #51, I highly (highly) recommend building some kind hardware abstraction layer so you don't have to think about the PCB components at this layer. Knob1 should always be knob1 at this level, regardless of what the hardware is. Period. If a knob1 doesn't exist, the daisy API should be able to handle that gracefully.

De/re-interleaving the audio shouldn't really be an issue. worst-case it takes a BLOCKSIZE loop on each side to prep the audio for the hardware. I'm not sure if the codec on the seed supports a non-interleaved format, but that won't be a problem in software.

Good to know. In FAUST, you can specify the render block size as an argument, so it can be aligned with the internal hardware.

PaulBatchelor commented 4 years ago

Have not forgotten about this. There's a lot of daisy construction going on, so I'm keen on waiting for the dust to settle.

Since we are moving towards C++, we may want to consider making a base daisy DSP class that FAUST could build on top of.

sletz commented 4 years ago

Daisy branch on Faust GitHub https://github.com/grame-cncm/faust/tree/daisy

stephenhensley commented 4 years ago

Since this is underway, and not necessarily tied to libdaisy development I'm going to close this.

Biggest thing is going to be some metadata, etc. on the hardware end of things which may not get tracked here.