FaustDSP Intergration

Hey everyone,

trying to work out FaustDSP intergration. I have successfully run and compiled this Faust example on the daisy seed: https://github.com/electro-smith/DaisySP/tree/master/examples/faustnoise

When substituting the FaustDSP code for a slightly tweaked standard example, I ran into problems with the sketch sounding at the wrong pitch.

Here’s what I did including the fix -

this code plays an saw at 440 on the left and at 660 on the right output -

declare filename "FaustSawtooth.dsp"; declare name "FaustSawtooth"; import("stdfaust.lib");
freq = nentry("freq",440,20,20000,0.01) : si.smoo;
gain = nentry("gain",1,0,1,0.01) : si.smoo;
process = os.sawtooth(freq)*gain,os.sawtooth(freq*1.5)*gain;

Here’s that code in the Faust IDE

I exported from the IDE with these settings - Platform: source Architecture: cplusplus

Then I took the .cpp file from FaustNoise example and replaced the user section in the architecture - between

/**************************BEGIN USER SECTION **************************/

and

/***************************END USER SECTION ***************************/

with the generated code from the IDE.

This ends up pretty distorted (alised) and playing one Octave too high.

To get the correct pitch, I had to change this
sample_rate = seed.AudioSampleRate();
to this
sample_rate = 96000;

To get rid of the aliasing, I had to set this
#define MY_BUFFER_SIZE 1

The preset size was 8; the higher the number the worse the distortion, the lower the better (tried 1, 2, 4, 8 , 16). 1 yields a good sounding result.

So I think there might be something of with the default sample rate and / or block size but I’m not sure … this should probably be looked at when making the Architecture Files for Faust-Daisy.

Here’s my “working” as in “sounding-correct” code - take the FaustNoise example and replace the content of exfaust.cpp with the code below, then compile.

/* ------------------------------------------------------------
author: "Grame"
copyright: "(c)GRAME 2009"
license: "BSD"
name: "Noise"
version: "1.1"
Code generated with Faust 2.23.4 (https://faust.grame.fr)
Compilation options: -lang cpp -scal -ftz 0
------------------------------------------------------------ */

#ifndef __mydsp_H__
#define __mydsp_H__

/************************************************************************
 IMPORTANT NOTE : this file contains two clearly delimited sections :
 the ARCHITECTURE section (in two parts) and the USER section. Each section
 is governed by its own copyright and license. Please check individually
 each section for license and copyright information.
 *************************************************************************/

/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/

/************************************************************************
 FAUST Architecture File
 Copyright (C) 2020 GRAME, Centre National de Creation Musicale
 ---------------------------------------------------------------------
 This Architecture section is free software; you can redistribute it
 and/or modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 3 of
 the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; If not, see <http://www.gnu.org/licenses/>.
 
 EXCEPTION : As a special exception, you may create a larger work
 that contains this FAUST architecture section and distribute
 that work under terms of your choice, so long as this FAUST
 architecture section is not modified.
 
 ************************************************************************
 ************************************************************************/

#include "daisysp.h"
#include "daisy_seed.h"

#include "faust/gui/UI.h"
#include "faust/dsp/dsp.h"

#ifdef MIDICTRL
#include "faust/midi/daisy-midi.h"
#endif

using namespace daisysp;
using namespace daisy;

struct Meta
{
    virtual ~Meta(){};
    virtual void declare(const char* key, const char* value) = 0;
};

/******************************************************************************
 *******************************************************************************
 
 VECTOR INTRINSICS
 
 *******************************************************************************
 *******************************************************************************/


/********************END ARCHITECTURE SECTION (part 1/2)****************/

/**************************BEGIN USER SECTION **************************/

#ifndef FAUSTFLOAT
#define FAUSTFLOAT float
#endif 

#include <algorithm>
#include <cmath>
#include <math.h>


#ifndef FAUSTCLASS 
#define FAUSTCLASS mydsp
#endif

#ifdef __APPLE__ 
#define exp10f __exp10f
#define exp10 __exp10
#endif

class mydsp : public dsp {
	
 private:
	
	FAUSTFLOAT fEntry0;
	float fRec0[2];
	int fSampleRate;
	float fConst0;
	float fConst1;
	FAUSTFLOAT fEntry1;
	float fRec3[2];
	float fRec1[2];
	float fRec4[2];
	
 public:
	
	void metadata(Meta* m) { 
		m->declare("filename", "FaustSawtooth.dsp");
		m->declare("maths.lib/author", "GRAME");
		m->declare("maths.lib/copyright", "GRAME");
		m->declare("maths.lib/license", "LGPL with exception");
		m->declare("maths.lib/name", "Faust Math Library");
		m->declare("maths.lib/version", "2.3");
		m->declare("name", "FaustSawtooth");
		m->declare("oscillators.lib/name", "Faust Oscillator Library");
		m->declare("oscillators.lib/version", "0.1");
		m->declare("platform.lib/name", "Generic Platform Library");
		m->declare("platform.lib/version", "0.1");
		m->declare("signals.lib/name", "Faust Signal Routing Library");
		m->declare("signals.lib/version", "0.0");
	}

	virtual int getNumInputs() {
		return 0;
	}
	virtual int getNumOutputs() {
		return 2;
	}
	virtual int getInputRate(int channel) {
		int rate;
		switch ((channel)) {
			default: {
				rate = -1;
				break;
			}
		}
		return rate;
	}
	virtual int getOutputRate(int channel) {
		int rate;
		switch ((channel)) {
			case 0: {
				rate = 1;
				break;
			}
			case 1: {
				rate = 1;
				break;
			}
			default: {
				rate = -1;
				break;
			}
		}
		return rate;
	}
	
	static void classInit(int sample_rate) {
	}
	
	virtual void instanceConstants(int sample_rate) {
		fSampleRate = sample_rate;
		fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate)));
		fConst1 = (1.0f / fConst0);
	}
	
	virtual void instanceResetUserInterface() {
		fEntry0 = FAUSTFLOAT(1.0f);
		fEntry1 = FAUSTFLOAT(440.0f);
	}
	
	virtual void instanceClear() {
		for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
			fRec0[l0] = 0.0f;
		}
		for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) {
			fRec3[l1] = 0.0f;
		}
		for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) {
			fRec1[l2] = 0.0f;
		}
		for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) {
			fRec4[l3] = 0.0f;
		}
	}
	
	virtual void init(int sample_rate) {
		classInit(sample_rate);
		instanceInit(sample_rate);
	}
	virtual void instanceInit(int sample_rate) {
		instanceConstants(sample_rate);
		instanceResetUserInterface();
		instanceClear();
	}
	
	virtual mydsp* clone() {
		return new mydsp();
	}
	
	virtual int getSampleRate() {
		return fSampleRate;
	}
	
	virtual void buildUserInterface(UI* ui_interface) {
		ui_interface->openVerticalBox("FaustSawtooth");
		ui_interface->addNumEntry("freq", &fEntry1, 440.0f, 20.0f, 20000.0f, 0.00999999978f);
		ui_interface->addNumEntry("gain", &fEntry0, 1.0f, 0.0f, 1.0f, 0.00999999978f);
		ui_interface->closeBox();
	}
	
	virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
		FAUSTFLOAT* output0 = outputs[0];
		FAUSTFLOAT* output1 = outputs[1];
		float fSlow0 = (0.00100000005f * float(fEntry0));
		float fSlow1 = (0.00100000005f * float(fEntry1));
		for (int i = 0; (i < count); i = (i + 1)) {
			fRec0[0] = (fSlow0 + (0.999000013f * fRec0[1]));
			fRec3[0] = (fSlow1 + (0.999000013f * fRec3[1]));
			float fTemp0 = std::max<float>(1.00000001e-07f, std::fabs(fRec3[0]));
			float fTemp1 = (fRec1[1] + (fConst1 * fTemp0));
			float fTemp2 = (fTemp1 + -1.0f);
			int iTemp3 = (fTemp2 < 0.0f);
			fRec1[0] = (iTemp3 ? fTemp1 : fTemp2);
			float fRec2 = (iTemp3 ? fTemp1 : (fTemp1 + ((1.0f - (fConst0 / fTemp0)) * fTemp2)));
			output0[i] = FAUSTFLOAT((fRec0[0] * ((2.0f * fRec2) + -1.0f)));
			float fTemp4 = std::max<float>(1.00000001e-07f, std::fabs((1.5f * fRec3[0])));
			float fTemp5 = (fRec4[1] + (fConst1 * fTemp4));
			float fTemp6 = (fTemp5 + -1.0f);
			int iTemp7 = (fTemp6 < 0.0f);
			fRec4[0] = (iTemp7 ? fTemp5 : fTemp6);
			float fRec5 = (iTemp7 ? fTemp5 : (fTemp5 + (fTemp6 * (1.0f - (fConst0 / fTemp4)))));
			output1[i] = FAUSTFLOAT((fRec0[0] * ((2.0f * fRec5) + -1.0f)));
			fRec0[1] = fRec0[0];
			fRec3[1] = fRec3[0];
			fRec1[1] = fRec1[0];
			fRec4[1] = fRec4[0];
		}
	}

};

/***************************END USER SECTION ***************************/

/*******************BEGIN ARCHITECTURE SECTION (part 2/2)***************/

FAUSTFLOAT* finputs[2];
FAUSTFLOAT* foutputs[2];

mydsp DSP;

#define MY_BUFFER_SIZE 1

static DaisySeed seed;

static void AudioCallback(float* in, float* out, size_t size)
{
    // Deinterleave
    for(size_t frame = 0; frame < size; frame++)
    {
        finputs[0][frame] = in[frame*2];
        finputs[1][frame] = in[frame*2+1];
    }

    // Faust processing
    DSP.compute(size, finputs, foutputs);

    // Interleave
    for(size_t frame = 0; frame < size; frame++)
    {
        out[frame*2] = foutputs[0][frame];
        out[frame*2+1] = foutputs[1][frame];
    }
}

int main(void)
{
    // initialize seed hardware and daisysp modules
    float sample_rate;
    seed.Configure();
    seed.Init();
    sample_rate = 96000; //seed.AudioSampleRate();

    // set buffer-size
    seed.SetAudioBlockSize(MY_BUFFER_SIZE);

    // allocate deinterleaved buffers
    finputs[0] = new FAUSTFLOAT[MY_BUFFER_SIZE];
    finputs[1] = new FAUSTFLOAT[MY_BUFFER_SIZE];

    foutputs[0] = new FAUSTFLOAT[MY_BUFFER_SIZE];
    foutputs[1] = new FAUSTFLOAT[MY_BUFFER_SIZE];

    // inti Faust DSP
    DSP.init(sample_rate);

#ifdef MIDICTRL
    daisy_midi midi_handler;
#endif


    // start callback
    seed.StartAudio(AudioCallback);


#ifdef MIDICTRL
    midi_handler.startMidi();
    ;
#endif

    while(1)
    {
#ifdef MIDICTRL
        midi_handler.processMidi();
#endif
    }
}

/********************END ARCHITECTURE SECTION (part 2/2)****************/

#endif

Here are the latest files I wrote https://github.com/grame-cncm/faust/tree/daisy/architecture/daisy (in “blind” mode, since we don’t have received our card yet).

1 Like

@sletz awesome thank you! Hope your Daisy arrives soon!

1 Like

Some improvements on the Faust GitHub daisy branch here: https://github.com/grame-cncm/faust/tree/daisy. You’ll have to use the faust2daisy tool described here: https://github.com/grame-cncm/faust/tree/daisy/architecture/daisy

Still no controllers for now, you can only compile simple generators or effects.

Testing welcome !

2 Likes

@sletz awesome! vacation coming up - so I can give this a whirl soon!

Some progress, and faust2daisy is described here: https://github.com/grame-cncm/faust/tree/daisy/architecture/daisy

1 Like

MIDI support to test, see: https://github.com/grame-cncm/faust/tree/daisy/architecture/daisy

(I don’t have adapted hardware to test here.)

2 Likes