Skip to content

Instantly share code, notes, and snippets.

@xaviergodart
Last active March 5, 2017 21:01
Show Gist options
  • Save xaviergodart/66e3b7ac9483735bb8ec1bba82ec923b to your computer and use it in GitHub Desktop.
Save xaviergodart/66e3b7ac9483735bb8ec1bba82ec923b to your computer and use it in GitHub Desktop.
GainCtl Faust generated juce cpp
//----------------------------------------------------------
// name: "GainCtl"
// version: "0.1"
// author: "Xavier Godart"
// copyright: "Xavier Godart"
//
// Code generated with Faust 0.9.96 (http://faust.grame.fr)
//----------------------------------------------------------
/* link with */
/************************************************************************
FAUST Architecture File
Copyright (C) 2016 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 "JuceLibraryCode/JuceHeader.h"
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2016 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.
************************************************************************
************************************************************************/
#ifndef __timed_dsp__
#define __timed_dsp__
/************************************************************************
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) 2003-2011 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.
************************************************************************
************************************************************************/
/******************************************************************************
*******************************************************************************
FAUST DSP
*******************************************************************************
*******************************************************************************/
#ifndef __dsp__
#define __dsp__
#ifndef FAUSTFLOAT
#define FAUSTFLOAT float
#endif
class UI;
struct Meta;
/**
* Signal processor definition.
*/
class dsp {
public:
dsp() {}
virtual ~dsp() {}
/* Return instance number of audio inputs */
virtual int getNumInputs() = 0;
/* Return instance number of audio outputs */
virtual int getNumOutputs() = 0;
/**
* Trigger the UI* parameter with instance specific calls
* to 'addBtton', 'addVerticalSlider'... in order to build the UI.
*
* @param ui_interface - the UI* user interface builder
*/
virtual void buildUserInterface(UI* ui_interface) = 0;
/* Returns the sample rate currently used by the instance */
virtual int getSampleRate() = 0;
/** Global init, calls the following methods:
* - static class 'classInit': static table initialisation
* - 'instanceInit': constants and instance table initialisation
*
* @param samplingRate - the sampling rate in Herz
*/
virtual void init(int samplingRate) = 0;
/** Init instance state
*
* @param samplingRate - the sampling rate in Herz
*/
virtual void instanceInit(int samplingRate) = 0;
/** Init instance constant state
*
* @param samplingRate - the sampling rate in Herz
*/
virtual void instanceConstants(int samplingRate) = 0;
/* Init default control parameters values */
virtual void instanceResetUserInterface() = 0;
/* Init instance state (delay lines...) */
virtual void instanceClear() = 0;
/**
* Return a clone of the instance.
*
* @return a copy of the instance on success, otherwise a null pointer.
*/
virtual dsp* clone() = 0;
/**
* Trigger the Meta* parameter with instance specific calls to 'declare' (key, value metadata).
*
* @param m - the Meta* meta user
*/
virtual void metadata(Meta* m) = 0;
/**
* DSP instance computation, to be called with sucessive in/out audio buffers.
*
* @param count - the nomber of frames to compute
* @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
* @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
*
*/
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0;
/**
* DSP instance computation: alternative method to be used by subclasses.
*
* @param date_usec - the timestamp in microsec given by audio driver.
* @param count - the nomber of frames to compute
* @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
* @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
*
*/
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
};
/**
* Generic DSP decorator.
*/
class decorator_dsp : public dsp {
protected:
dsp* fDSP;
public:
decorator_dsp(dsp* dsp = 0):fDSP(dsp) {}
virtual ~decorator_dsp() { delete fDSP; }
virtual int getNumInputs() { return fDSP->getNumInputs(); }
virtual int getNumOutputs() { return fDSP->getNumOutputs(); }
virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); }
virtual int getSampleRate() { return fDSP->getSampleRate(); }
virtual void init(int samplingRate) { fDSP->init(samplingRate); }
virtual void instanceInit(int samplingRate) { fDSP->instanceInit(samplingRate); }
virtual void instanceConstants(int samplingRate) { fDSP->instanceConstants(samplingRate); }
virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); }
virtual void instanceClear() { fDSP->instanceClear(); }
virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); }
virtual void metadata(Meta* m) { return fDSP->metadata(m); }
// Beware: subclasses usually have to overload the two 'compute' methods
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); }
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); }
};
/**
* On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
* flags to avoid costly denormals.
*/
#ifdef __SSE__
#include <xmmintrin.h>
#ifdef __SSE2__
#define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
#else
#define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
#endif
#else
#define AVOIDDENORMALS
#endif
#endif
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2016 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.
************************************************************************
************************************************************************/
#ifndef FAUST_GUI_H
#define FAUST_GUI_H
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2016 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.
************************************************************************
************************************************************************/
#ifndef FAUST_UI_H
#define FAUST_UI_H
#ifndef FAUSTFLOAT
#define FAUSTFLOAT float
#endif
/*******************************************************************************
* UI : Faust User Interface
* This abstract class contains only the method that the faust compiler can
* generate to describe a DSP interface.
******************************************************************************/
class UI
{
public:
UI() {}
virtual ~UI() {}
// -- widget's layouts
virtual void openTabBox(const char* label) = 0;
virtual void openHorizontalBox(const char* label) = 0;
virtual void openVerticalBox(const char* label) = 0;
virtual void closeBox() = 0;
// -- active widgets
virtual void addButton(const char* label, FAUSTFLOAT* zone) = 0;
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) = 0;
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) = 0;
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) = 0;
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) = 0;
// -- passive widgets
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) = 0;
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) = 0;
// -- metadata declarations
virtual void declare(FAUSTFLOAT*, const char*, const char*) {}
};
//----------------------------------------------------------------
// Generic decorator
//----------------------------------------------------------------
class DecoratorUI : public UI
{
protected:
UI* fUI;
public:
DecoratorUI(UI* ui = 0):fUI(ui)
{}
virtual ~DecoratorUI() { delete fUI; }
// -- widget's layouts
virtual void openTabBox(const char* label) { fUI->openTabBox(label); }
virtual void openHorizontalBox(const char* label) { fUI->openHorizontalBox(label); }
virtual void openVerticalBox(const char* label) { fUI->openVerticalBox(label); }
virtual void closeBox() { fUI->closeBox(); }
// -- active widgets
virtual void addButton(const char* label, FAUSTFLOAT* zone) { fUI->addButton(label, zone); }
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) { fUI->addCheckButton(label, zone); }
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{ fUI->addVerticalSlider(label, zone, init, min, max, step); }
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{ fUI->addHorizontalSlider(label, zone, init, min, max, step); }
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{ fUI->addNumEntry(label, zone, init, min, max, step); }
// -- passive widgets
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{ fUI->addHorizontalBargraph(label, zone, min, max); }
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{ fUI->addVerticalBargraph(label, zone, min, max); }
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) { fUI->declare(zone, key, val); }
};
//----------------------------------------------------------------
// BaseUI with empty methods
//----------------------------------------------------------------
class BaseUI : public UI
{
public:
// -- widget's layouts
virtual void openTabBox(const char* label) {};
virtual void openHorizontalBox(const char* label) {}
virtual void openVerticalBox(const char* label) {}
virtual void closeBox() {}
// -- active widgets
virtual void addButton(const char* label, FAUSTFLOAT* zone) = 0;
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) = 0;
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) {}
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) {}
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) {}
// -- passive widgets
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) {}
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) {}
// -- metadata declarations
virtual void declare(FAUSTFLOAT*, const char*, const char*) {}
};
#endif
/*
Copyright (C) 2000 Paul Davis
Copyright (C) 2003 Rohan Drape
Copyright (C) 2016 GRAME (renaming for internal use)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
ISO/POSIX C version of Paul Davis's lock free ringbuffer C++ code.
This is safe for the case of one read thread and one write thread.
*/
#ifndef __ring_buffer__
#define __ring_buffer__
#include <stdlib.h>
#include <string.h>
typedef struct {
char *buf;
size_t len;
}
ringbuffer_data_t;
typedef struct {
char *buf;
volatile size_t write_ptr;
volatile size_t read_ptr;
size_t size;
size_t size_mask;
int mlocked;
}
ringbuffer_t;
ringbuffer_t *ringbuffer_create(size_t sz);
void ringbuffer_free(ringbuffer_t *rb);
void ringbuffer_get_read_vector(const ringbuffer_t *rb,
ringbuffer_data_t *vec);
void ringbuffer_get_write_vector(const ringbuffer_t *rb,
ringbuffer_data_t *vec);
size_t ringbuffer_read(ringbuffer_t *rb, char *dest, size_t cnt);
size_t ringbuffer_peek(ringbuffer_t *rb, char *dest, size_t cnt);
void ringbuffer_read_advance(ringbuffer_t *rb, size_t cnt);
size_t ringbuffer_read_space(const ringbuffer_t *rb);
int ringbuffer_mlock(ringbuffer_t *rb);
void ringbuffer_reset(ringbuffer_t *rb);
void ringbuffer_reset_size (ringbuffer_t * rb, size_t sz);
size_t ringbuffer_write(ringbuffer_t *rb, const char *src,
size_t cnt);
void ringbuffer_write_advance(ringbuffer_t *rb, size_t cnt);
size_t ringbuffer_write_space(const ringbuffer_t *rb);
/* Create a new ringbuffer to hold at least `sz' bytes of data. The
actual buffer size is rounded up to the next power of two. */
inline ringbuffer_t *
ringbuffer_create (size_t sz)
{
size_t power_of_two;
ringbuffer_t *rb;
if ((rb = (ringbuffer_t *) malloc (sizeof (ringbuffer_t))) == NULL) {
return NULL;
}
for (power_of_two = 1u; 1u << power_of_two < sz; power_of_two++);
rb->size = 1u << power_of_two;
rb->size_mask = rb->size;
rb->size_mask -= 1;
rb->write_ptr = 0;
rb->read_ptr = 0;
if ((rb->buf = (char *) malloc (rb->size)) == NULL) {
free (rb);
return NULL;
}
rb->mlocked = 0;
return rb;
}
/* Free all data associated with the ringbuffer `rb'. */
inline void
ringbuffer_free (ringbuffer_t * rb)
{
#ifdef USE_MLOCK
if (rb->mlocked) {
munlock (rb->buf, rb->size);
}
#endif /* USE_MLOCK */
free (rb->buf);
free (rb);
}
/* Lock the data block of `rb' using the system call 'mlock'. */
inline int
ringbuffer_mlock (ringbuffer_t * rb)
{
#ifdef USE_MLOCK
if (mlock (rb->buf, rb->size)) {
return -1;
}
#endif /* USE_MLOCK */
rb->mlocked = 1;
return 0;
}
/* Reset the read and write pointers to zero. This is not thread
safe. */
inline void
ringbuffer_reset (ringbuffer_t * rb)
{
rb->read_ptr = 0;
rb->write_ptr = 0;
memset(rb->buf, 0, rb->size);
}
/* Reset the read and write pointers to zero. This is not thread
safe. */
inline void
ringbuffer_reset_size (ringbuffer_t * rb, size_t sz)
{
rb->size = sz;
rb->size_mask = rb->size;
rb->size_mask -= 1;
rb->read_ptr = 0;
rb->write_ptr = 0;
}
/* Return the number of bytes available for reading. This is the
number of bytes in front of the read pointer and behind the write
pointer. */
inline size_t
ringbuffer_read_space (const ringbuffer_t * rb)
{
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
if (w > r) {
return w - r;
} else {
return (w - r + rb->size) & rb->size_mask;
}
}
/* Return the number of bytes available for writing. This is the
number of bytes in front of the write pointer and behind the read
pointer. */
inline size_t
ringbuffer_write_space (const ringbuffer_t * rb)
{
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
if (w > r) {
return ((r - w + rb->size) & rb->size_mask) - 1;
} else if (w < r) {
return (r - w) - 1;
} else {
return rb->size - 1;
}
}
/* The copying data reader. Copy at most `cnt' bytes from `rb' to
`dest'. Returns the actual number of bytes copied. */
inline size_t
ringbuffer_read (ringbuffer_t * rb, char *dest, size_t cnt)
{
size_t free_cnt;
size_t cnt2;
size_t to_read;
size_t n1, n2;
if ((free_cnt = ringbuffer_read_space (rb)) == 0) {
return 0;
}
to_read = cnt > free_cnt ? free_cnt : cnt;
cnt2 = rb->read_ptr + to_read;
if (cnt2 > rb->size) {
n1 = rb->size - rb->read_ptr;
n2 = cnt2 & rb->size_mask;
} else {
n1 = to_read;
n2 = 0;
}
memcpy (dest, &(rb->buf[rb->read_ptr]), n1);
rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask;
if (n2) {
memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2);
rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask;
}
return to_read;
}
/* The copying data reader w/o read pointer advance. Copy at most
`cnt' bytes from `rb' to `dest'. Returns the actual number of bytes
copied. */
inline size_t
ringbuffer_peek (ringbuffer_t * rb, char *dest, size_t cnt)
{
size_t free_cnt;
size_t cnt2;
size_t to_read;
size_t n1, n2;
size_t tmp_read_ptr;
tmp_read_ptr = rb->read_ptr;
if ((free_cnt = ringbuffer_read_space (rb)) == 0) {
return 0;
}
to_read = cnt > free_cnt ? free_cnt : cnt;
cnt2 = tmp_read_ptr + to_read;
if (cnt2 > rb->size) {
n1 = rb->size - tmp_read_ptr;
n2 = cnt2 & rb->size_mask;
} else {
n1 = to_read;
n2 = 0;
}
memcpy (dest, &(rb->buf[tmp_read_ptr]), n1);
tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask;
if (n2) {
memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2);
}
return to_read;
}
/* The copying data writer. Copy at most `cnt' bytes to `rb' from
`src'. Returns the actual number of bytes copied. */
inline size_t
ringbuffer_write (ringbuffer_t * rb, const char *src, size_t cnt)
{
size_t free_cnt;
size_t cnt2;
size_t to_write;
size_t n1, n2;
if ((free_cnt = ringbuffer_write_space (rb)) == 0) {
return 0;
}
to_write = cnt > free_cnt ? free_cnt : cnt;
cnt2 = rb->write_ptr + to_write;
if (cnt2 > rb->size) {
n1 = rb->size - rb->write_ptr;
n2 = cnt2 & rb->size_mask;
} else {
n1 = to_write;
n2 = 0;
}
memcpy (&(rb->buf[rb->write_ptr]), src, n1);
rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask;
if (n2) {
memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2);
rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask;
}
return to_write;
}
/* Advance the read pointer `cnt' places. */
inline void
ringbuffer_read_advance (ringbuffer_t * rb, size_t cnt)
{
size_t tmp = (rb->read_ptr + cnt) & rb->size_mask;
rb->read_ptr = tmp;
}
/* Advance the write pointer `cnt' places. */
inline void
ringbuffer_write_advance (ringbuffer_t * rb, size_t cnt)
{
size_t tmp = (rb->write_ptr + cnt) & rb->size_mask;
rb->write_ptr = tmp;
}
/* The non-copying data reader. `vec' is an array of two places. Set
the values at `vec' to hold the current readable data at `rb'. If
the readable data is in one segment the second segment has zero
length. */
inline void
ringbuffer_get_read_vector (const ringbuffer_t * rb,
ringbuffer_data_t * vec)
{
size_t free_cnt;
size_t cnt2;
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
if (w > r) {
free_cnt = w - r;
} else {
free_cnt = (w - r + rb->size) & rb->size_mask;
}
cnt2 = r + free_cnt;
if (cnt2 > rb->size) {
/* Two part vector: the rest of the buffer after the current write
ptr, plus some from the start of the buffer. */
vec[0].buf = &(rb->buf[r]);
vec[0].len = rb->size - r;
vec[1].buf = rb->buf;
vec[1].len = cnt2 & rb->size_mask;
} else {
/* Single part vector: just the rest of the buffer */
vec[0].buf = &(rb->buf[r]);
vec[0].len = free_cnt;
vec[1].len = 0;
}
}
/* The non-copying data writer. `vec' is an array of two places. Set
the values at `vec' to hold the current writeable data at `rb'. If
the writeable data is in one segment the second segment has zero
length. */
inline void
ringbuffer_get_write_vector (const ringbuffer_t * rb,
ringbuffer_data_t * vec)
{
size_t free_cnt;
size_t cnt2;
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
if (w > r) {
free_cnt = ((r - w + rb->size) & rb->size_mask) - 1;
} else if (w < r) {
free_cnt = (r - w) - 1;
} else {
free_cnt = rb->size - 1;
}
cnt2 = w + free_cnt;
if (cnt2 > rb->size) {
/* Two part vector: the rest of the buffer after the current write
ptr, plus some from the start of the buffer. */
vec[0].buf = &(rb->buf[w]);
vec[0].len = rb->size - w;
vec[1].buf = rb->buf;
vec[1].len = cnt2 & rb->size_mask;
} else {
vec[0].buf = &(rb->buf[w]);
vec[0].len = free_cnt;
vec[1].len = 0;
}
}
#endif // __ring_buffer__
#include <list>
#include <map>
#include <vector>
/*******************************************************************************
* GUI : Abstract Graphic User Interface
* Provides additional mechanisms to synchronize widgets and zones. Widgets
* should both reflect the value of a zone and allow to change this value.
******************************************************************************/
class uiItem;
typedef void (*uiCallback)(FAUSTFLOAT val, void* data);
class clist : public std::list<uiItem*>
{
public:
virtual ~clist();
};
typedef std::map<FAUSTFLOAT*, clist*> zmap;
typedef std::map<FAUSTFLOAT*, ringbuffer_t*> ztimedmap;
class GUI : public UI
{
private:
static std::list<GUI*> fGuiList;
zmap fZoneMap;
bool fStopped;
public:
GUI() : fStopped(false)
{
fGuiList.push_back(this);
}
virtual ~GUI()
{
// delete all
zmap::iterator g;
for (g = fZoneMap.begin(); g != fZoneMap.end(); g++) {
delete (*g).second;
}
// suppress 'this' in static fGuiList
fGuiList.remove(this);
}
// -- registerZone(z,c) : zone management
void registerZone(FAUSTFLOAT* z, uiItem* c)
{
if (fZoneMap.find(z) == fZoneMap.end()) fZoneMap[z] = new clist();
fZoneMap[z]->push_back(c);
}
void updateAllZones();
void updateZone(FAUSTFLOAT* z);
static void updateAllGuis()
{
std::list<GUI*>::iterator g;
for (g = fGuiList.begin(); g != fGuiList.end(); g++) {
(*g)->updateAllZones();
}
}
void addCallback(FAUSTFLOAT* zone, uiCallback foo, void* data);
virtual void show() {};
virtual bool run() { return false; };
virtual void stop() { fStopped = true; }
bool stopped() { return fStopped; }
// -- widget's layouts
virtual void openTabBox(const char* label) {};
virtual void openHorizontalBox(const char* label) {}
virtual void openVerticalBox(const char* label) {}
virtual void closeBox() {}
// -- active widgets
virtual void addButton(const char* label, FAUSTFLOAT* zone) {}
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) {}
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) {}
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) {}
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) {}
// -- passive widgets
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) {}
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) {}
// -- metadata declarations
virtual void declare(FAUSTFLOAT* , const char* , const char*) {}
// Static global for timed zones, shared between all UI that will set timed values
static ztimedmap gTimedZoneMap;
};
/**
* User Interface Item: abstract definition
*/
class uiItem
{
protected:
GUI* fGUI;
FAUSTFLOAT* fZone;
FAUSTFLOAT fCache;
uiItem(GUI* ui, FAUSTFLOAT* zone):fGUI(ui), fZone(zone), fCache(FAUSTFLOAT(-123456.654321))
{
ui->registerZone(zone, this);
}
public:
virtual ~uiItem()
{}
void modifyZone(FAUSTFLOAT v)
{
fCache = v;
if (*fZone != v) {
*fZone = v;
fGUI->updateZone(fZone);
}
}
FAUSTFLOAT cache() { return fCache; }
virtual void reflectZone() = 0;
};
/**
* User Interface item owned (and so deleted) by external code
*/
class uiOwnedItem : public uiItem {
protected:
uiOwnedItem(GUI* ui, FAUSTFLOAT* zone):uiItem(ui, zone)
{}
public:
virtual ~uiOwnedItem()
{}
virtual void reflectZone() {}
};
/**
* Callback Item
*/
struct uiCallbackItem : public uiItem
{
uiCallback fCallback;
void* fData;
uiCallbackItem(GUI* ui, FAUSTFLOAT* zone, uiCallback foo, void* data)
: uiItem(ui, zone), fCallback(foo), fData(data) {}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
fCallback(v, fData);
}
};
/**
* Allows to group a set of zones.
*/
class uiGroupItem : public uiItem
{
protected:
std::vector<FAUSTFLOAT*> fZoneMap;
public:
uiGroupItem(GUI* ui, FAUSTFLOAT* zone):uiItem(ui, zone)
{}
virtual ~uiGroupItem()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
// Update all zones of the same group
std::vector<FAUSTFLOAT*>::iterator it;
for (it = fZoneMap.begin(); it != fZoneMap.end(); it++) {
(*(*it)) = v;
}
}
void addZone(FAUSTFLOAT* zone) { fZoneMap.push_back(zone); }
};
/**
* Update all user items reflecting zone z
*/
inline void GUI::updateZone(FAUSTFLOAT* z)
{
FAUSTFLOAT v = *z;
clist* l = fZoneMap[z];
for (clist::iterator c = l->begin(); c != l->end(); c++) {
if ((*c)->cache() != v) (*c)->reflectZone();
}
}
/**
* Update all user items not up to date
*/
inline void GUI::updateAllZones()
{
for (zmap::iterator m = fZoneMap.begin(); m != fZoneMap.end(); m++) {
FAUSTFLOAT* z = m->first;
clist* l = m->second;
if (z) {
FAUSTFLOAT v = *z;
for (clist::iterator c = l->begin(); c != l->end(); c++) {
if ((*c)->cache() != v) (*c)->reflectZone();
}
}
}
}
inline void GUI::addCallback(FAUSTFLOAT* zone, uiCallback foo, void* data)
{
new uiCallbackItem(this, zone, foo, data);
};
inline clist::~clist()
{
std::list<uiItem*>::iterator it;
for (it = begin(); it != end(); it++) {
uiOwnedItem* owned = dynamic_cast<uiOwnedItem*>(*it);
// owned items are deleted by external code
if (!owned) {
delete (*it);
}
}
}
// For precise timestamped control
struct DatedControl {
double fDate;
FAUSTFLOAT fValue;
DatedControl(double d = 0., FAUSTFLOAT v = FAUSTFLOAT(0)):fDate(d), fValue(v) {}
};
#endif
#include <set>
#include <float.h>
#include <assert.h>
namespace {
#if __APPLE__
#if TARGET_OS_IPHONE
//inline double GetCurrentTimeInUsec() { return double(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000.; }
// TODO
inline double GetCurrentTimeInUsec() { return 0.0; }
#else
#include <CoreAudio/HostTime.h>
inline double GetCurrentTimeInUsec() { return double(AudioConvertHostTimeToNanos(AudioGetCurrentHostTime())) / 1000.; }
#endif
#endif
#if __linux__
#include <sys/time.h>
inline double GetCurrentTimeInUsec()
{
struct timeval tv;
(void)gettimeofday(&tv, (struct timezone *)NULL);
return double((tv.tv_sec * 1000000) + tv.tv_usec);
}
#endif
#if _WIN32
#include <Windows.h>
inline double GetCurrentTimeInUsec(void)
{
LARGE_INTEGER time;
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&time);
return double(time.QuadPart) / double(frequency.QuadPart) * 1000000.0;
}
#endif
}
/**
* ZoneUI : this class collect zones in a set.
*/
struct ZoneUI : public UI
{
std::set<FAUSTFLOAT*> fZoneSet;
ZoneUI() {};
virtual ~ZoneUI() {};
void insertZone(FAUSTFLOAT* zone)
{
if (GUI::gTimedZoneMap.find(zone) != GUI::gTimedZoneMap.end()) {
fZoneSet.insert(zone);
}
}
// -- widget's layouts
void openTabBox(const char* label)
{}
void openHorizontalBox(const char* label)
{}
void openVerticalBox(const char* label)
{}
void closeBox()
{}
// -- active widgets
void addButton(const char* label, FAUSTFLOAT* zone)
{
insertZone(zone);
}
void addCheckButton(const char* label, FAUSTFLOAT* zone)
{
insertZone(zone);
}
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step)
{
insertZone(zone);
}
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step)
{
insertZone(zone);
}
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step)
{
insertZone(zone);
}
// -- passive widgets
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax)
{
insertZone(zone);
}
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax)
{
insertZone(zone);
}
// -- metadata declarations
void declare(FAUSTFLOAT* zone, const char* key, const char* val)
{}
};
/**
* Timed signal processor that allows to handle the decorated DSP by 'slices'
* that is, calling the 'compute' method several times and changing control
* parameters between slices.
*/
class timed_dsp : public decorator_dsp {
protected:
double fDateUsec; // Compute call date in usec
double fOffsetUsec; // Compute call offset in usec
bool fFirstCallback;
ZoneUI fZoneUI;
void computeSlice(int offset, int slice, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
if (slice > 0) {
FAUSTFLOAT** inputs_slice = (FAUSTFLOAT**)alloca(fDSP->getNumInputs() * sizeof(FAUSTFLOAT*));
for (int chan = 0; chan < fDSP->getNumInputs(); chan++) {
inputs_slice[chan] = &(inputs[chan][offset]);
}
FAUSTFLOAT** outputs_slice = (FAUSTFLOAT**)alloca(fDSP->getNumOutputs() * sizeof(FAUSTFLOAT*));
for (int chan = 0; chan < fDSP->getNumOutputs(); chan++) {
outputs_slice[chan] = &(outputs[chan][offset]);
}
fDSP->compute(slice, inputs_slice, outputs_slice);
}
}
double convertUsecToSample(double usec)
{
return std::max<double>(0., (double(getSampleRate()) * (usec - fDateUsec)) / 1000000.);
}
ztimedmap::iterator getNextControl(DatedControl& res, bool convert_ts)
{
DatedControl date1(DBL_MAX, 0);
ztimedmap::iterator it1, it2 = GUI::gTimedZoneMap.end();
std::set<FAUSTFLOAT*>::iterator it3;
// Find date of next audio slice to compute
for (it3 = fZoneUI.fZoneSet.begin(); it3 != fZoneUI.fZoneSet.end(); it3++) {
// If value list is not empty, get the date and keep the minimal one
it1 = GUI::gTimedZoneMap.find(*it3);
if (it1 != GUI::gTimedZoneMap.end()) { // Check if zone still in global GUI::gTimedZoneMap (since MidiUI may have been desallocated)
DatedControl date2;
if (ringbuffer_peek((*it1).second, (char*)&date2, sizeof(DatedControl)) == sizeof(DatedControl)
&& date2.fDate < date1.fDate) {
it2 = it1;
date1 = date2;
}
}
}
// If needed, convert date1 in samples from begining of the buffer, possible moving to 0 (if negative)
if (convert_ts) {
date1.fDate = convertUsecToSample(date1.fDate);
}
res = date1;
return it2;
}
virtual void computeAux(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs, bool convert_ts)
{
int slice, offset = 0;
ztimedmap::iterator it;
DatedControl next_control;
// Do audio computation "slice" by "slice"
while ((it = getNextControl(next_control, convert_ts)) != GUI::gTimedZoneMap.end()) {
// Compute audio slice
slice = int(next_control.fDate) - offset;
computeSlice(offset, slice, inputs, outputs);
offset += slice;
// Update control
ringbuffer_t* control_values = (*it).second;
*((*it).first) = next_control.fValue;
// Move ringbuffer pointer
ringbuffer_read_advance(control_values, sizeof(DatedControl));
}
// Compute last audio slice
slice = count - offset;
computeSlice(offset, slice, inputs, outputs);
}
public:
timed_dsp(dsp* dsp):decorator_dsp(dsp), fDateUsec(0), fOffsetUsec(0), fFirstCallback(true)
{}
virtual ~timed_dsp()
{}
virtual void init(int samplingRate)
{
fDSP->init(samplingRate);
}
virtual void buildUserInterface(UI* ui_interface)
{
fDSP->buildUserInterface(ui_interface);
// Only keep zones that are in GUI::gTimedZoneMap
fDSP->buildUserInterface(&fZoneUI);
}
virtual timed_dsp* clone()
{
return new timed_dsp(fDSP->clone());
}
// Default method take a timestamp at 'compute' call time
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
compute(::GetCurrentTimeInUsec(), count, inputs, outputs);
}
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
if (date_usec == -1) {
// Timestamp is expressed in frames
computeAux(count, inputs, outputs, false);
} else {
// Save the timestamp offset in the first callback
if (fFirstCallback) {
fOffsetUsec = ::GetCurrentTimeInUsec() - date_usec;
fDateUsec = date_usec + fOffsetUsec;
fFirstCallback = false;
}
// RtMidi mode : timestamp must be converted in frames
computeAux(count, inputs, outputs, true);
// Keep call date
fDateUsec = date_usec + fOffsetUsec;
}
}
};
#endif
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2011 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.
************************************************************************
************************************************************************/
#ifndef FAUST_MAPUI_H
#define FAUST_MAPUI_H
#ifndef FAUSTFLOAT
#define FAUSTFLOAT float
#endif
#include <vector>
#include <map>
#include <string>
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2011 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.
************************************************************************
************************************************************************/
#ifndef FAUST_PATHBUILDER_H
#define FAUST_PATHBUILDER_H
#include <vector>
#include <string>
#include <algorithm>
/*******************************************************************************
* PathBuilder : Faust User Interface
* Helper class to build complete hierarchical path for UI items.
******************************************************************************/
class PathBuilder
{
protected:
std::vector<std::string> fControlsLevel;
public:
PathBuilder() {}
virtual ~PathBuilder() {}
std::string buildPath(const std::string& label)
{
std::string res = "/";
for (size_t i = 0; i < fControlsLevel.size(); i++) {
res += fControlsLevel[i];
res += "/";
}
res += label;
replace(res.begin(), res.end(), ' ', '_');
return res;
}
};
#endif // FAUST_PATHBUILDER_H
/*******************************************************************************
* MapUI : Faust User Interface
* This class creates a map of complete hierarchical path and zones for each UI items.
******************************************************************************/
class MapUI : public UI, public PathBuilder
{
protected:
// Complete path map
std::map<std::string, FAUSTFLOAT*> fPathZoneMap;
// Label zone map
std::map<std::string, FAUSTFLOAT*> fLabelZoneMap;
public:
MapUI() {};
virtual ~MapUI() {};
// -- widget's layouts
void openTabBox(const char* label)
{
fControlsLevel.push_back(label);
}
void openHorizontalBox(const char* label)
{
fControlsLevel.push_back(label);
}
void openVerticalBox(const char* label)
{
fControlsLevel.push_back(label);
}
void closeBox()
{
fControlsLevel.pop_back();
}
// -- active widgets
void addButton(const char* label, FAUSTFLOAT* zone)
{
fPathZoneMap[buildPath(label)] = zone;
fLabelZoneMap[label] = zone;
}
void addCheckButton(const char* label, FAUSTFLOAT* zone)
{
fPathZoneMap[buildPath(label)] = zone;
fLabelZoneMap[label] = zone;
}
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step)
{
fPathZoneMap[buildPath(label)] = zone;
fLabelZoneMap[label] = zone;
}
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step)
{
fPathZoneMap[buildPath(label)] = zone;
fLabelZoneMap[label] = zone;
}
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step)
{
fPathZoneMap[buildPath(label)] = zone;
fLabelZoneMap[label] = zone;
}
// -- passive widgets
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax)
{
fPathZoneMap[buildPath(label)] = zone;
fLabelZoneMap[label] = zone;
}
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax)
{
fPathZoneMap[buildPath(label)] = zone;
fLabelZoneMap[label] = zone;
}
// -- metadata declarations
void declare(FAUSTFLOAT* zone, const char* key, const char* val)
{}
// set/get
void setParamValue(const std::string& path, FAUSTFLOAT value)
{
if (fPathZoneMap.find(path) != fPathZoneMap.end()) {
*fPathZoneMap[path] = value;
} else if (fLabelZoneMap.find(path) != fLabelZoneMap.end()) {
*fLabelZoneMap[path] = value;
}
}
FAUSTFLOAT getParamValue(const std::string& path)
{
if (fPathZoneMap.find(path) != fPathZoneMap.end()) {
return *fPathZoneMap[path];
} else if (fLabelZoneMap.find(path) != fLabelZoneMap.end()) {
return *fLabelZoneMap[path];
} else {
return FAUSTFLOAT(0);
}
}
// map access
std::map<std::string, FAUSTFLOAT*>& getMap() { return fPathZoneMap; }
int getParamsCount() { return fPathZoneMap.size(); }
std::string getParamAddress(int index)
{
std::map<std::string, FAUSTFLOAT*>::iterator it = fPathZoneMap.begin();
while (index-- > 0 && it++ != fPathZoneMap.end()) {}
return (*it).first;
}
};
#endif // FAUST_MAPUI_H
/************************************************************************
************************************************************************
FAUST Architecture File
Copyright (C) 2003-2011 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/>.
************************************************************************
************************************************************************/
#ifndef __misc__
#define __misc__
#include <algorithm>
#include <map>
#include <string.h>
#include <stdlib.h>
/************************************************************************
************************************************************************
FAUST Architecture File
Copyright (C) 2003-2011 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/>.
************************************************************************
************************************************************************/
#ifndef __meta__
#define __meta__
struct Meta
{
virtual void declare(const char* key, const char* value) = 0;
virtual ~Meta() {};
};
#endif
using std::max;
using std::min;
struct XXXX_Meta : std::map<const char*, const char*>
{
void declare(const char* key, const char* value) { (*this)[key]=value; }
};
struct MY_Meta : Meta, std::map<const char*, const char*>
{
void declare(const char* key, const char* value) { (*this)[key]=value; }
};
inline int lsr(int x, int n) { return int(((unsigned int)x) >> n); }
inline int int2pow2(int x) { int r = 0; while ((1<<r) < x) r++; return r; }
inline long lopt(char* argv[], const char* name, long def)
{
int i;
for (i = 0; argv[i]; i++) if (!strcmp(argv[i], name)) return atoi(argv[i+1]);
return def;
}
inline bool isopt(char* argv[], const char* name)
{
int i;
for (i = 0; argv[i]; i++) if (!strcmp(argv[i], name)) return true;
return false;
}
inline const char* lopts(char* argv[], const char* name, const char* def)
{
int i;
for (i = 0; argv[i]; i++) if (!strcmp(argv[i], name)) return argv[i+1];
return def;
}
#endif
/************************************************************************
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) 2003-2011 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.
************************************************************************
************************************************************************/
#ifndef __dsp_adapter__
#define __dsp_adapter__
#include <string.h>
class dsp_adapter : public decorator_dsp {
private:
FAUSTFLOAT** fAdaptedInputs;
FAUSTFLOAT** fAdaptedOutputs;
int fHardwareInputs;
int fHardwareOutputs;
void adaptBuffers(FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
for (int i = 0; i < fHardwareInputs; i++) {
fAdaptedInputs[i] = inputs[i];
}
for (int i = 0; i < fHardwareOutputs; i++) {
fAdaptedOutputs[i] = outputs[i];
}
}
public:
dsp_adapter(dsp* dsp, int hardware_inputs, int hardware_outputs, int buffer_size):decorator_dsp(dsp)
{
fHardwareInputs = hardware_inputs;
fHardwareOutputs = hardware_outputs;
fAdaptedInputs = new FAUSTFLOAT*[dsp->getNumInputs()];
for (int i = 0; i < dsp->getNumInputs() - fHardwareInputs; i++) {
fAdaptedInputs[i + fHardwareInputs] = new FAUSTFLOAT[buffer_size];
memset(fAdaptedInputs[i + fHardwareInputs], 0, sizeof(FAUSTFLOAT) * buffer_size);
}
fAdaptedOutputs = new FAUSTFLOAT*[dsp->getNumOutputs()];
for (int i = 0; i < dsp->getNumOutputs() - fHardwareOutputs; i++) {
fAdaptedOutputs[i + fHardwareOutputs] = new FAUSTFLOAT[buffer_size];
memset(fAdaptedOutputs[i + fHardwareOutputs], 0, sizeof(FAUSTFLOAT) * buffer_size);
}
}
virtual ~dsp_adapter()
{
for (int i = 0; i < fDSP->getNumInputs() - fHardwareInputs; i++) {
delete [] fAdaptedInputs[i + fHardwareInputs];
}
delete [] fAdaptedInputs;
for (int i = 0; i < fDSP->getNumOutputs() - fHardwareOutputs; i++) {
delete [] fAdaptedOutputs[i + fHardwareOutputs];
}
delete [] fAdaptedOutputs;
}
virtual int getNumInputs() { return fHardwareInputs; }
virtual int getNumOutputs() { return fHardwareOutputs; }
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
adaptBuffers(inputs, outputs);
fDSP->compute(date_usec, count, fAdaptedInputs, fAdaptedOutputs);
}
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
adaptBuffers(inputs, outputs);
fDSP->compute(count, fAdaptedInputs, fAdaptedOutputs);
}
};
#endif
/************************************************************************
FAUST Architecture File
Copyright (C) 2016 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.
************************************************************************
************************************************************************/
#ifndef FAUSTFLOAT
#define FAUSTFLOAT float
#endif
#include "../JuceLibraryCode/JuceHeader.h"
#include <stack>
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2016 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.
************************************************************************
************************************************************************/
#ifndef MetaData_UI_H
#define MetaData_UI_H
#include <map>
#include <set>
#include <string>
#include <assert.h>
/************************************************************************
************************************************************************
FAUST compiler
Copyright (C) 2003-2015 GRAME, Centre National de Creation Musicale
---------------------------------------------------------------------
This program 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 2 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
************************************************************************
************************************************************************/
#ifndef SIMPLEPARSER_H
#define SIMPLEPARSER_H
// ---------------------------------------------------------------------
// Simple Parser
// A parser returns true if it was able to parse what it is
// supposed to parse and advance the pointer. Otherwise it returns false
// and the pointer is not advanced so that another parser can be tried.
// ---------------------------------------------------------------------
#include <vector>
#include <map>
#include <string>
#include <fstream>
#include <iostream>
#include <ctype.h>
using namespace std;
struct itemInfo {
std::string type;
std::string label;
std::string address;
std::string init;
std::string min;
std::string max;
std::string step;
std::vector<std::pair<std::string, std::string> > meta;
};
bool parseMenuList(const char*& p, vector<string>& names, vector<double>& values);
bool parseMenuItem(const char*& p, string& name, double& value);
void skipBlank(const char*& p);
bool parseChar(const char*& p, char x);
bool parseWord(const char*& p, const char* w);
bool parseString(const char*& p, char quote, string& s);
bool parseSQString(const char*& p, string& s);
bool parseDQString(const char*& p, string& s);
bool parseDouble(const char*& p, double& x);
// ---------------------------------------------------------------------
//
// IMPLEMENTATION
//
// ---------------------------------------------------------------------
/**
* @brief parseMenuList, parse a menu list {'low' : 440.0; 'mid' : 880.0; 'hi' : 1760.0}...
* @param p the string to parse, then the remaining string
* @param names the vector of names found
* @param values the vector of values found
* @return true if a menu list was found
*/
inline bool parseMenuList(const char*& p, vector<string>& names, vector<double>& values)
{
vector<string> tmpnames;
vector<double> tmpvalues;
const char* saved = p;
if (parseChar(p, '{')) {
do {
string n;
double v;
if (parseMenuItem(p, n, v)) {
tmpnames.push_back(n);
tmpvalues.push_back(v);
} else {
p = saved;
return false;
}
} while (parseChar(p, ';'));
if (parseChar(p, '}')) {
// we suceeded
names = tmpnames;
values = tmpvalues;
return true;
}
}
p = saved;
return false;
}
/**
* @brief parseMenuItem, parse a menu item ...'low':440.0...
* @param p the string to parse, then the remaining string
* @param name the name found
* @param value the value found
* @return true if a nemu item was found
*/
inline bool parseMenuItem(const char*& p, string& name, double& value)
{
const char* saved = p;
if (parseSQString(p, name) && parseChar(p, ':') && parseDouble(p, value)) {
return true;
} else {
p = saved;
return false;
}
}
// ---------------------------------------------------------------------
// Elementary parsers
// ---------------------------------------------------------------------
// Report a parsing error
static bool parseError(const char*& p, const char* errmsg)
{
std::cerr << "Parse error : " << errmsg << " here : " << p << std::endl;
return true;
}
// Parse character x, but don't report error if fails
static bool tryChar(const char*& p, char x)
{
skipBlank(p);
if (x == *p) {
p++;
return true;
} else {
return false;
}
}
/**
* @brief skipBlank : advance pointer p to the first non blank character
* @param p the string to parse, then the remaining string
*/
inline void skipBlank(const char*& p)
{
while (isspace(*p)) { p++; }
}
/**
* @brief parseChar : parse a specific character x
* @param p the string to parse, then the remaining string
* @param x the character to recognize
* @return true if x was found at the begin of p
*/
inline bool parseChar(const char*& p, char x)
{
skipBlank(p);
if (x == *p) {
p++;
return true;
} else {
return false;
}
}
/**
* @brief parseWord : parse a specific string w
* @param p the string to parse, then the remaining string
* @param w the string to recognize
* @return true if string w was found at the begin of p
*/
inline bool parseWord(const char*& p, const char* w)
{
skipBlank(p);
const char* saved = p;
while ((*w == *p) && (*w)) {++w; ++p;}
if (*w) {
p = saved;
return false;
} else {
return true;
}
}
/**
* @brief parseDouble : parse number [s]dddd[.dddd] and store the result in x
* @param p the string to parse, then the remaining string
* @param x the float number found if any
* @return true if a float number was found at the begin of p
*/
inline bool parseDouble(const char*& p, double& x)
{
double sign = +1.0; // sign of the number
double ipart = 0; // integral part of the number
double dpart = 0; // decimal part of the number before division
double dcoef = 1.0; // division factor for the decimal part
bool valid = false; // true if the number contains at least one digit
skipBlank(p);
const char* saved = p; // to restore position if we fail
if (parseChar(p, '+')) {
sign = 1.0;
} else if (parseChar(p, '-')) {
sign = -1.0;
}
while (isdigit(*p)) {
valid = true;
ipart = ipart*10 + (*p - '0');
p++;
}
if (parseChar(p, '.')) {
while (isdigit(*p)) {
valid = true;
dpart = dpart*10 + (*p - '0');
dcoef *= 10.0;
p++;
}
}
if (valid) {
x = sign*(ipart + dpart/dcoef);
} else {
p = saved;
}
return valid;
}
/**
* @brief parseString, parse an arbitrary quoted string q...q and store the result in s
* @param p the string to parse, then the remaining string
* @param quote the character used to quote the string
* @param s the (unquoted) string found if any
* @return true if a string was found at the begin of p
*/
inline bool parseString(const char*& p, char quote, string& s)
{
string str;
skipBlank(p);
const char* saved = p;
if (*p++ == quote) {
while ((*p != 0) && (*p != quote)) {
str += *p++;
}
if (*p++ == quote) {
s = str;
return true;
}
}
p = saved;
return false;
}
/**
* @brief parseSQString, parse a single quoted string '...' and store the result in s
* @param p the string to parse, then the remaining string
* @param s the (unquoted) string found if any
* @return true if a string was found at the begin of p
*/
inline bool parseSQString(const char*& p, string& s)
{
return parseString(p, '\'', s);
}
/**
* @brief parseDQString, parse a double quoted string "..." and store the result in s
* @param p the string to parse, then the remaining string
* @param s the (unquoted) string found if any
* @return true if a string was found at the begin of p
*/
inline bool parseDQString(const char*& p, string& s)
{
return parseString(p, '"', s);
}
static bool parseMetaData(const char*& p, std::map<std::string, std::string>& metadatas)
{
std::string metaKey, metaValue;
if (parseChar(p, ':') && parseChar(p, '[')) {
do {
if (parseChar(p, '{') && parseDQString(p, metaKey) && parseChar(p, ':') && parseDQString(p, metaValue) && parseChar(p, '}')) {
metadatas[metaKey] = metaValue;
}
} while (tryChar(p, ','));
return parseChar(p, ']');
} else {
return false;
}
}
static bool parseItemMetaData(const char*& p, std::vector<std::pair<std::string, std::string> >& metadatas)
{
std::string metaKey, metaValue;
if (parseChar(p, ':') && parseChar(p, '[')) {
do {
if (parseChar(p, '{') && parseDQString(p, metaKey) && parseChar(p, ':') && parseDQString(p, metaValue) && parseChar(p, '}')) {
metadatas.push_back(std::make_pair(metaKey, metaValue));
}
} while (tryChar(p, ','));
return parseChar(p, ']');
} else {
return false;
}
}
// ---------------------------------------------------------------------
// Parse metadatas of the interface:
// "name" : "...", "inputs" : "...", "outputs" : "...", ...
// and store the result as key/value
//
static bool parseGlobalMetaData(const char*& p, std::string& key, std::string& value, std::map<std::string, std::string>& metadatas)
{
if (parseDQString(p, key)) {
if (key == "meta") {
return parseMetaData(p, metadatas);
} else {
return parseChar(p, ':') && parseDQString(p, value);
}
} else {
return false;
}
}
// ---------------------------------------------------------------------
// Parse gui:
// "type" : "...", "label" : "...", "address" : "...", ...
// and store the result in uiItems Vector
//
static bool parseUI(const char*& p, std::vector<itemInfo*>& uiItems, int& numItems)
{
if (parseChar(p, '{')) {
std::string label;
std::string value;
do {
if (parseDQString(p, label)) {
if (label == "type") {
if (uiItems.size() != 0) {
numItems++;
}
if (parseChar(p, ':') && parseDQString(p, value)) {
itemInfo* item = new itemInfo;
item->type = value;
uiItems.push_back(item);
}
}
else if (label == "label") {
if (parseChar(p, ':') && parseDQString(p, value)) {
itemInfo* item = uiItems[numItems];
item->label = value;
}
}
else if (label == "address") {
if (parseChar(p, ':') && parseDQString(p, value)) {
itemInfo* item = uiItems[numItems];
item->address = value;
}
}
else if (label == "meta") {
itemInfo* item = uiItems[numItems];
if (!parseItemMetaData(p, item->meta)) {
return false;
}
}
else if (label == "init") {
if (parseChar(p, ':') && parseDQString(p, value)) {
itemInfo* item = uiItems[numItems];
item->init = value;
}
}
else if (label == "min") {
if (parseChar(p, ':') && parseDQString(p, value)) {
itemInfo* item = uiItems[numItems];
item->min = value;
}
}
else if (label == "max") {
if (parseChar(p, ':') && parseDQString(p, value)) {
itemInfo* item = uiItems[numItems];
item->max = value;
}
}
else if (label == "step"){
if (parseChar(p, ':') && parseDQString(p, value)) {
itemInfo* item = uiItems[numItems];
item->step = value;
}
}
else if (label == "items") {
if (parseChar(p, ':') && parseChar(p, '[')) {
do {
if (!parseUI(p, uiItems, numItems)) {
return false;
}
} while (tryChar(p, ','));
if (parseChar(p, ']')) {
itemInfo* item = new itemInfo;
item->type = "close";
uiItems.push_back(item);
numItems++;
}
}
}
} else {
return false;
}
} while (tryChar(p, ','));
return parseChar(p, '}');
} else {
return false;
}
}
// ---------------------------------------------------------------------
// Parse full JSON record describing a JSON/Faust interface :
// {"metadatas": "...", "ui": [{ "type": "...", "label": "...", "items": [...], "address": "...","init": "...", "min": "...", "max": "...","step": "..."}]}
//
// and store the result in map Metadatas and vector containing the items of the interface. Returns true if parsing was successfull.
//
inline bool parseJson(const char*& p, std::map<std::string, std::string>& metadatas, std::vector<itemInfo*>& uiItems)
{
parseChar(p, '{');
do {
std::string key;
std::string value;
if (parseGlobalMetaData(p, key, value, metadatas)) {
if (key != "meta") {
// keep "name", "inputs", "outputs" key/value pairs
metadatas[key] = value;
}
} else if (key == "ui") {
int numItems = 0;
parseChar(p, '[') && parseUI(p, uiItems, numItems);
}
} while (tryChar(p, ','));
return parseChar(p, '}');
}
#endif // SIMPLEPARSER_H
#ifndef FAUSTFLOAT
#define FAUSTFLOAT float
#endif
static inline bool startWith(const std::string& str, const std::string& prefix)
{
return (str.substr(0, prefix.size()) == prefix);
}
/**
* Convert a dB value into a scale between 0 and 1 (following IEC standard ?)
*/
static inline FAUSTFLOAT dB2Scale(FAUSTFLOAT dB)
{
FAUSTFLOAT scale = FAUSTFLOAT(1.0);
/*if (dB < -70.0f)
scale = 0.0f;
else*/
if (dB < FAUSTFLOAT(-60.0))
scale = (dB + FAUSTFLOAT(70.0)) * FAUSTFLOAT(0.0025);
else if (dB < FAUSTFLOAT(-50.0))
scale = (dB + FAUSTFLOAT(60.0)) * FAUSTFLOAT(0.005) + FAUSTFLOAT(0.025);
else if (dB < FAUSTFLOAT(-40.0))
scale = (dB + FAUSTFLOAT(50.0)) * FAUSTFLOAT(0.0075) + FAUSTFLOAT(0.075);
else if (dB < FAUSTFLOAT(-30.0))
scale = (dB + FAUSTFLOAT(40.0)) * FAUSTFLOAT(0.015) + FAUSTFLOAT(0.15);
else if (dB < FAUSTFLOAT(-20.0))
scale = (dB + FAUSTFLOAT(30.0)) * FAUSTFLOAT(0.02) + FAUSTFLOAT(0.3);
else if (dB < FAUSTFLOAT(-0.001) || dB > FAUSTFLOAT(0.001)) /* if (dB < 0.0) */
scale = (dB + FAUSTFLOAT(20.0)) * FAUSTFLOAT(0.025) + FAUSTFLOAT(0.5);
return scale;
}
/*******************************************************************************
* MetaDataUI : Common class for MetaData handling
******************************************************************************/
//============================= BEGIN GROUP LABEL METADATA===========================
// Unlike widget's label, metadata inside group's label are not extracted directly by
// the Faust compiler. Therefore they must be extracted within the architecture file
//-----------------------------------------------------------------------------------
class MetaDataUI {
protected:
std::string fGroupTooltip;
std::map<FAUSTFLOAT*, FAUSTFLOAT> fGuiSize; // map widget zone with widget size coef
std::map<FAUSTFLOAT*, std::string> fTooltip; // map widget zone with tooltip strings
std::map<FAUSTFLOAT*, std::string> fUnit; // map widget zone to unit string (i.e. "dB")
std::map<FAUSTFLOAT*, std::string> fRadioDescription; // map zone to {'low':440; ...; 'hi':1000.0}
std::map<FAUSTFLOAT*, std::string> fMenuDescription; // map zone to {'low':440; ...; 'hi':1000.0}
std::set<FAUSTFLOAT*> fKnobSet; // set of widget zone to be knobs
std::set<FAUSTFLOAT*> fLedSet; // set of widget zone to be LEDs
std::set<FAUSTFLOAT*> fNumSet; // set of widget zone to be numerical bargraphs
std::set<FAUSTFLOAT*> fLogSet; // set of widget zone having a log UI scale
std::set<FAUSTFLOAT*> fExpSet; // set of widget zone having an exp UI scale
void clearMetadata()
{
fGuiSize.clear();
fTooltip.clear();
fUnit.clear();
fRadioDescription.clear();
fMenuDescription.clear();
fKnobSet.clear();
fLedSet.clear();
fNumSet.clear();
fLogSet.clear();
fExpSet.clear();
}
bool isKnob(FAUSTFLOAT* zone)
{
return fKnobSet.count(zone) > 0;
}
bool isRadio(FAUSTFLOAT* zone)
{
return fRadioDescription.count(zone) > 0;
}
bool isMenu(FAUSTFLOAT* zone)
{
return fMenuDescription.count(zone) > 0;
}
bool isLed(FAUSTFLOAT* zone)
{
return fLedSet.count(zone) > 0;
}
bool isNumerical(FAUSTFLOAT* zone)
{
return fNumSet.count(zone) > 0;
}
/**
* rmWhiteSpaces(): Remove the leading and trailing white spaces of a string
* (but not those in the middle of the string)
*/
std::string rmWhiteSpaces(const std::string& s)
{
size_t i = s.find_first_not_of(" \t");
size_t j = s.find_last_not_of(" \t");
if ((i != std::string::npos) && (j != std::string::npos)) {
return s.substr(i, 1+j-i);
} else {
return "";
}
}
/**
* Extracts metdata from a label : 'vol [unit: dB]' -> 'vol' + metadata(unit=dB)
*/
void extractMetadata(const std::string& fulllabel, std::string& label, std::map<std::string, std::string>& metadata)
{
enum {kLabel, kEscape1, kEscape2, kEscape3, kKey, kValue};
int state = kLabel; int deep = 0;
std::string key, value;
for (unsigned int i = 0; i < fulllabel.size(); i++) {
char c = fulllabel[i];
switch (state) {
case kLabel :
assert(deep == 0);
switch (c) {
case '\\' : state = kEscape1; break;
case '[' : state = kKey; deep++; break;
default : label += c;
}
break;
case kEscape1:
label += c;
state = kLabel;
break;
case kEscape2:
key += c;
state = kKey;
break;
case kEscape3:
value += c;
state = kValue;
break;
case kKey:
assert(deep > 0);
switch (c) {
case '\\':
state = kEscape2;
break;
case '[':
deep++;
key += c;
break;
case ':':
if (deep == 1) {
state = kValue;
} else {
key += c;
}
break;
case ']':
deep--;
if (deep < 1) {
metadata[rmWhiteSpaces(key)] = "";
state = kLabel;
key="";
value="";
} else {
key += c;
}
break;
default : key += c;
}
break;
case kValue:
assert(deep > 0);
switch (c) {
case '\\':
state = kEscape3;
break;
case '[':
deep++;
value += c;
break;
case ']':
deep--;
if (deep < 1) {
metadata[rmWhiteSpaces(key)] = rmWhiteSpaces(value);
state = kLabel;
key = "";
value = "";
} else {
value += c;
}
break;
default : value += c;
}
break;
default:
std::cerr << "ERROR unrecognized state " << state << std::endl;
}
}
label = rmWhiteSpaces(label);
}
/**
* Format tooltip string by replacing some white spaces by
* return characters so that line width doesn't exceed n.
* Limitation : long words exceeding n are not cut
*/
std::string formatTooltip(int n, const std::string& tt)
{
std::string ss = tt; // ss string we are going to format
int lws = 0; // last white space encountered
int lri = 0; // last return inserted
for (int i = 0; i < (int)tt.size(); i++) {
if (tt[i] == ' ') lws = i;
if (((i-lri) >= n) && (lws > lri)) {
// insert return here
ss[lws] = '\n';
lri = lws;
}
}
return ss;
}
public:
virtual ~MetaDataUI()
{}
enum Scale {
kLin,
kLog,
kExp
};
Scale getScale(FAUSTFLOAT* zone)
{
if (fLogSet.count(zone) > 0) return kLog;
if (fExpSet.count(zone) > 0) return kExp;
return kLin;
}
/**
* Analyses the widget zone metadata declarations and takes appropriate actions
*/
void declare(FAUSTFLOAT* zone, const char* key, const char* value)
{
if (zone == 0) {
// special zone 0 means group metadata
if (strcmp(key,"tooltip") == 0) {
// only group tooltip are currently implemented
fGroupTooltip = formatTooltip(30, value);
}
} else {
if (strcmp(key,"size") == 0) {
fGuiSize[zone] = atof(value);
}
else if (strcmp(key,"tooltip") == 0) {
fTooltip[zone] = formatTooltip(30, value);
}
else if (strcmp(key,"unit") == 0) {
fUnit[zone] = value ;
}
else if (strcmp(key,"scale") == 0) {
if (strcmp(value,"log") == 0) {
fLogSet.insert(zone);
} else if (strcmp(value,"exp") == 0) {
fExpSet.insert(zone);
}
}
else if (strcmp(key,"style") == 0) {
if (strcmp(value,"knob") == 0) {
fKnobSet.insert(zone);
} else if (strcmp(value,"led") == 0) {
fLedSet.insert(zone);
} else if (strcmp(value,"numerical") == 0) {
fNumSet.insert(zone);
} else {
const char* p = value;
if (parseWord(p, "radio")) {
fRadioDescription[zone] = std::string(p);
} else if (parseWord(p, "menu")) {
fMenuDescription[zone] = std::string(p);
}
}
}
}
}
};
#endif
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2016 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.
************************************************************************
************************************************************************/
#ifndef __ValueConverter__
#define __ValueConverter__
/***************************************************************************************
ValueConverter.h
(GRAME, © 2015)
Set of conversion objects used to map user interface values (for example a gui slider
delivering values between 0 and 1) to faust values (for example a vslider between
20 and 20000) using a log scale.
-- Utilities
Range(lo,hi) : clip a value x between lo and hi
Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2
Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2
-- Value Converters
ValueConverter::ui2faust(x)
ValueConverter::faust2ui(x)
-- ValueConverters used for sliders depending of the scale
LinearValueConverter(umin, umax, fmin, fmax)
LogValueConverter(umin, umax, fmin, fmax)
ExpValueConverter(umin, umax, fmin, fmax)
-- ValueConverters used for accelerometers based on 3 points
AccUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 0
AccDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 1
AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2
AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3
-- lists of ZoneControl are used to implement accelerometers metadata for each axes
ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter
-- ZoneReader are used to implement screencolor metadata
ZoneReader(zone, valueConverter) : a zone with a data converter
****************************************************************************************/
#include <float.h>
#include <algorithm> // std::max
#include <cmath>
#include <vector>
#include <assert.h>
//--------------------------------------------------------------------------------------
// Interpolator(lo,hi,v1,v2)
// Maps a value x between lo and hi to a value y between v1 and v2
// y = v1 + (x-lo)/(hi-lo)*(v2-v1)
// y = v1 + (x-lo) * coef with coef = (v2-v1)/(hi-lo)
// y = v1 + x*coef - lo*coef
// y = v1 - lo*coef + x*coef
// y = offset + x*coef with offset = v1 - lo*coef
//--------------------------------------------------------------------------------------
class Interpolator
{
private:
//--------------------------------------------------------------------------------------
// Range(lo,hi) clip a value between lo and hi
//--------------------------------------------------------------------------------------
struct Range
{
double fLo;
double fHi;
Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {}
double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; }
};
Range fRange;
double fCoef;
double fOffset;
public:
Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi)
{
if (hi != lo) {
// regular case
fCoef = (v2-v1)/(hi-lo);
fOffset = v1 - lo*fCoef;
} else {
// degenerate case, avoids division by zero
fCoef = 0;
fOffset = (v1+v2)/2;
}
}
double operator()(double v)
{
double x = fRange(v);
return fOffset + x*fCoef;
}
void getLowHigh(double& amin, double& amax)
{
amin = fRange.fLo;
amax = fRange.fHi;
}
};
//--------------------------------------------------------------------------------------
// Interpolator3pt(lo,mi,hi,v1,vm,v2)
// Map values between lo mid hi to values between v1 vm v2
//--------------------------------------------------------------------------------------
class Interpolator3pt
{
private:
Interpolator fSegment1;
Interpolator fSegment2;
double fMid;
public:
Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) :
fSegment1(lo, mi, v1, vm),
fSegment2(mi, hi, vm, v2),
fMid(mi) {}
double operator()(double x) { return (x < fMid) ? fSegment1(x) : fSegment2(x); }
void getMappingValues(double& amin, double& amid, double& amax)
{
fSegment1.getLowHigh(amin, amid);
fSegment2.getLowHigh(amid, amax);
}
};
//--------------------------------------------------------------------------------------
// Abstract ValueConverter class. Converts values between UI and Faust representations
//--------------------------------------------------------------------------------------
class ValueConverter
{
public:
virtual ~ValueConverter() {}
virtual double ui2faust(double x) = 0;
virtual double faust2ui(double x) = 0;
};
//--------------------------------------------------------------------------------------
// Linear conversion between ui and faust values
//--------------------------------------------------------------------------------------
class LinearValueConverter : public ValueConverter
{
private:
Interpolator fUI2F;
Interpolator fF2UI;
public:
LinearValueConverter(double umin, double umax, double fmin, double fmax) :
fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax)
{}
LinearValueConverter() :
fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.)
{}
virtual double ui2faust(double x) { return fUI2F(x); }
virtual double faust2ui(double x) { return fF2UI(x); }
};
//--------------------------------------------------------------------------------------
// Logarithmic conversion between ui and faust values
//--------------------------------------------------------------------------------------
class LogValueConverter : public LinearValueConverter
{
public:
LogValueConverter(double umin, double umax, double fmin, double fmax) :
LinearValueConverter(umin, umax, log(std::max<double>(DBL_MIN, fmin)), log(std::max<double>(DBL_MIN, fmax)))
{}
virtual double ui2faust(double x) { return exp(LinearValueConverter::ui2faust(x)); }
virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(log(std::max<double>(x, DBL_MIN))); }
};
//--------------------------------------------------------------------------------------
// Exponential conversion between ui and Faust values
//--------------------------------------------------------------------------------------
class ExpValueConverter : public LinearValueConverter
{
public:
ExpValueConverter(double umin, double umax, double fmin, double fmax) :
LinearValueConverter(umin, umax, exp(fmin), exp(fmax))
{}
virtual double ui2faust(double x) { return log(LinearValueConverter::ui2faust(x)); }
virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(exp(x)); }
};
//--------------------------------------------------------------------------------------
// A converter than can be updated
//--------------------------------------------------------------------------------------
class UpdatableValueConverter : public ValueConverter {
protected:
bool fActive;
public:
UpdatableValueConverter():fActive(true)
{}
virtual ~UpdatableValueConverter()
{}
virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0;
virtual void getMappingValues(double& amin, double& amid, double& amax) = 0;
void setActive(bool on_off) { fActive = on_off; }
bool getActive() { return fActive; }
};
//--------------------------------------------------------------------------------------
// Convert accelerometer or gyroscope values to Faust values
// Using an Up curve (curve 0)
//--------------------------------------------------------------------------------------
class AccUpConverter : public UpdatableValueConverter
{
private:
Interpolator3pt fA2F;
Interpolator3pt fF2A;
public:
AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
fA2F(amin,amid,amax,fmin,fmid,fmax),
fF2A(fmin,fmid,fmax,amin,amid,amax)
{}
virtual double ui2faust(double x) { return fA2F(x); }
virtual double faust2ui(double x) { return fF2A(x); }
virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
{
//__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
fA2F = Interpolator3pt(amin,amid,amax,fmin,fmid,fmax);
fF2A = Interpolator3pt(fmin,fmid,fmax,amin,amid,amax);
}
virtual void getMappingValues(double& amin, double& amid, double& amax)
{
fA2F.getMappingValues(amin, amid, amax);
}
};
//--------------------------------------------------------------------------------------
// Convert accelerometer or gyroscope values to Faust values
// Using a Down curve (curve 1)
//--------------------------------------------------------------------------------------
class AccDownConverter : public UpdatableValueConverter
{
private:
Interpolator3pt fA2F;
Interpolator3pt fF2A;
public:
AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
fA2F(amin,amid,amax,fmax,fmid,fmin),
fF2A(fmin,fmid,fmax,amax,amid,amin)
{}
virtual double ui2faust(double x) { return fA2F(x); }
virtual double faust2ui(double x) { return fF2A(x); }
virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
{
//__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
fA2F = Interpolator3pt(amin,amid,amax,fmax,fmid,fmin);
fF2A = Interpolator3pt(fmin,fmid,fmax,amax,amid,amin);
}
virtual void getMappingValues(double& amin, double& amid, double& amax)
{
fA2F.getMappingValues(amin, amid, amax);
}
};
//--------------------------------------------------------------------------------------
// Convert accelerometer or gyroscope values to Faust values
// Using an Up-Down curve (curve 2)
//--------------------------------------------------------------------------------------
class AccUpDownConverter : public UpdatableValueConverter
{
private:
Interpolator3pt fA2F;
Interpolator fF2A;
public:
AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
fA2F(amin,amid,amax,fmin,fmax,fmin),
fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotone function
{}
virtual double ui2faust(double x) { return fA2F(x); }
virtual double faust2ui(double x) { return fF2A(x); }
virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
{
//__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
fA2F = Interpolator3pt(amin,amid,amax,fmin,fmax,fmin);
fF2A = Interpolator(fmin,fmax,amin,amax);
}
virtual void getMappingValues(double& amin, double& amid, double& amax)
{
fA2F.getMappingValues(amin, amid, amax);
}
};
//--------------------------------------------------------------------------------------
// Convert accelerometer or gyroscope values to Faust values
// Using a Down-Up curve (curve 3)
//--------------------------------------------------------------------------------------
class AccDownUpConverter : public UpdatableValueConverter
{
private:
Interpolator3pt fA2F;
Interpolator fF2A;
public:
AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) :
fA2F(amin,amid,amax,fmax,fmin,fmax),
fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotone function
{}
virtual double ui2faust(double x) { return fA2F(x); }
virtual double faust2ui(double x) { return fF2A(x); }
virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax)
{
//__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax);
fA2F = Interpolator3pt(amin,amid,amax,fmax,fmin,fmax);
fF2A = Interpolator(fmin,fmax,amin,amax);
}
virtual void getMappingValues(double& amin, double& amid, double& amax)
{
fA2F.getMappingValues(amin, amid, amax);
}
};
//--------------------------------------------------------------------------------------
// Base class for ZoneControl
//--------------------------------------------------------------------------------------
class ZoneControl
{
protected:
FAUSTFLOAT* fZone;
public:
ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {}
virtual ~ZoneControl() {}
virtual void update(double v) {}
virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {}
virtual void getMappingValues(double& amin, double& amid, double& amax) {}
FAUSTFLOAT* getZone() { return fZone; }
virtual void setActive(bool on_off) {}
virtual bool getActive() { return false; }
virtual int getCurve() { return -1; }
};
//--------------------------------------------------------------------------------------
// Useful to implement accelerometers metadata as a list of ZoneControl for each axes
//--------------------------------------------------------------------------------------
class ConverterZoneControl : public ZoneControl
{
private:
ValueConverter* fValueConverter;
public:
ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* valueConverter) : ZoneControl(zone), fValueConverter(valueConverter) {}
virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere...
void update(double v) { *fZone = fValueConverter->ui2faust(v); }
ValueConverter* getConverter() { return fValueConverter; }
};
//--------------------------------------------------------------------------------------
// Association of a zone and a four value converter, each one for each possible curve.
// Useful to implement accelerometers metadata as a list of ZoneControl for each axes
//--------------------------------------------------------------------------------------
class CurveZoneControl : public ZoneControl
{
private:
std::vector<UpdatableValueConverter*> fValueConverters;
int fCurve;
public:
CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0)
{
assert(curve >= 0 && curve <= 3);
fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max));
fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max));
fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max));
fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max));
fCurve = curve;
}
virtual ~CurveZoneControl()
{
std::vector<UpdatableValueConverter*>::iterator it;
for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
delete(*it);
}
}
void update(double v) { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); }
void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max)
{
fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max);
fCurve = curve;
}
void getMappingValues(double& amin, double& amid, double& amax)
{
fValueConverters[fCurve]->getMappingValues(amin, amid, amax);
}
void setActive(bool on_off)
{
std::vector<UpdatableValueConverter*>::iterator it;
for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) {
(*it)->setActive(on_off);
}
}
int getCurve() { return fCurve; }
};
class ZoneReader
{
private:
FAUSTFLOAT* fZone;
Interpolator fInterpolator;
public:
ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {}
virtual ~ZoneReader() {}
int getValue() {
if (fZone != 0) {
return (int)fInterpolator(*fZone);
} else {
return 127;
}
}
};
#endif
// Definition of the standard size of the different elements
#define kKnobWidth 100
#define kKnobHeight 100
#define kVSliderWidth 80
#define kVSliderHeight 250
#define kHSliderWidth 350
#define kHSliderHeight 50
#define kButtonWidth 100
#define kButtonHeight 50
#define kCheckButtonWidth 60
#define kCheckButtonHeight 40
#define kMenuWidth 100
#define kMenuHeight 50
#define kRadioButtonWidth 100
#define kRadioButtonHeight 55
#define kNumEntryWidth 100
#define kNumEntryHeight 50
#define kNumDisplayWidth 75
#define kNumDisplayHeight 50
#define kVBargraphWidth 60
#define kVBargraphHeight 250
#define kHBargraphWidth 350
#define kHBargraphHeight 50
#define kLedWidth 25
#define kLedHeight 25
#define kNameHeight 14
#define kMargin 4
/**
* \brief Custom LookAndFeel class.
* \details Define the appearance of all the JUCE widgets.
*/
struct CustomLookAndFeel : public LookAndFeel_V3
{
void drawRoundThumb (Graphics& g, const float x, const float y,
const float diameter, const Colour& colour, float outlineThickness)
{
const juce::Rectangle<float> a (x, y, diameter, diameter);
const float halfThickness = outlineThickness * 0.5f;
Path p;
p.addEllipse (x + halfThickness, y + halfThickness, diameter - outlineThickness, diameter - outlineThickness);
const DropShadow ds (Colours::black, 1, Point<int> (0, 0));
ds.drawForPath (g, p);
g.setColour (colour);
g.fillPath (p);
g.setColour (colour.brighter());
g.strokePath (p, PathStrokeType (outlineThickness));
}
void drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour,
bool isMouseOverButton, bool isButtonDown) override
{
Colour baseColour (backgroundColour.withMultipliedSaturation (button.hasKeyboardFocus (true) ? 1.3f : 0.9f)
.withMultipliedAlpha (button.isEnabled() ? 0.9f : 0.5f));
if (isButtonDown || isMouseOverButton)
baseColour = baseColour.contrasting (isButtonDown ? 0.2f : 0.1f);
const bool flatOnLeft = button.isConnectedOnLeft();
const bool flatOnRight = button.isConnectedOnRight();
const bool flatOnTop = button.isConnectedOnTop();
const bool flatOnBottom = button.isConnectedOnBottom();
const float width = button.getWidth() - 1.0f;
const float height = button.getHeight() - 1.0f;
if (width > 0 && height > 0)
{
const float cornerSize = jmin(15.0f, jmin(width, height) * 0.45f);
const float lineThickness = cornerSize * 0.1f;
const float halfThickness = lineThickness * 0.5f;
Path outline;
outline.addRoundedRectangle (0.5f + halfThickness, 0.5f + halfThickness, width - lineThickness, height - lineThickness,
cornerSize, cornerSize,
! (flatOnLeft || flatOnTop),
! (flatOnRight || flatOnTop),
! (flatOnLeft || flatOnBottom),
! (flatOnRight || flatOnBottom));
const Colour outlineColour (button.findColour (button.getToggleState() ? TextButton::textColourOnId
: TextButton::textColourOffId));
g.setColour (baseColour);
g.fillPath (outline);
if (! button.getToggleState()) {
g.setColour (outlineColour);
g.strokePath (outline, PathStrokeType (lineThickness));
}
}
}
void drawTickBox (Graphics& g, Component& component,
float x, float y, float w, float h,
bool ticked,
bool isEnabled,
bool isMouseOverButton,
bool isButtonDown) override
{
const float boxSize = w * 0.7f;
bool isDownOrDragging = component.isEnabled() && (component.isMouseOverOrDragging() || component.isMouseButtonDown());
const Colour colour (component.findColour (TextButton::buttonColourId).withMultipliedSaturation ((component.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha (component.isEnabled() ? 1.0f : 0.7f));
drawRoundThumb (g, x, y + (h - boxSize) * 0.5f, boxSize, colour,
isEnabled ? ((isButtonDown || isMouseOverButton) ? 1.1f : 0.5f) : 0.3f);
if (ticked) {
const Path tick (LookAndFeel_V2::getTickShape (6.0f));
g.setColour (isEnabled ? findColour (TextButton::buttonOnColourId) : Colours::grey);
const float scale = 9.0f;
const AffineTransform trans (AffineTransform::scale (w / scale, h / scale)
.translated (x - 2.5f, y + 1.0f));
g.fillPath (tick, trans);
}
}
void drawLinearSliderThumb (Graphics& g, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle style, Slider& slider) override
{
const float sliderRadius = (float)(getSliderThumbRadius (slider) - 2);
bool isDownOrDragging = slider.isEnabled() && (slider.isMouseOverOrDragging() || slider.isMouseButtonDown());
Colour knobColour (slider.findColour (Slider::thumbColourId).withMultipliedSaturation ((slider.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
.withMultipliedAlpha (slider.isEnabled() ? 1.0f : 0.7f));
if (style == Slider::LinearHorizontal || style == Slider::LinearVertical) {
float kx, ky;
if (style == Slider::LinearVertical) {
kx = x + width * 0.5f;
ky = sliderPos;
} else {
kx = sliderPos;
ky = y + height * 0.5f;
}
const float outlineThickness = slider.isEnabled() ? 0.8f : 0.3f;
drawRoundThumb (g,
kx - sliderRadius,
ky - sliderRadius,
sliderRadius * 2.0f,
knobColour, outlineThickness);
} else {
// Just call the base class for the demo
LookAndFeel_V2::drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
}
}
void drawLinearSlider (Graphics& g, int x, int y, int width, int height,
float sliderPos, float minSliderPos, float maxSliderPos,
const Slider::SliderStyle style, Slider& slider) override
{
g.fillAll (slider.findColour (Slider::backgroundColourId));
if (style == Slider::LinearBar || style == Slider::LinearBarVertical) {
const float fx = (float)x, fy = (float)y, fw = (float)width, fh = (float)height;
Path p;
if (style == Slider::LinearBarVertical)
p.addRectangle (fx, sliderPos, fw, 1.0f + fh - sliderPos);
else
p.addRectangle (fx, fy, sliderPos - fx, fh);
Colour baseColour (slider.findColour (Slider::rotarySliderFillColourId)
.withMultipliedSaturation (slider.isEnabled() ? 1.0f : 0.5f)
.withMultipliedAlpha (0.8f));
g.setColour (baseColour);
g.fillPath (p);
const float lineThickness = jmin(15.0f, jmin(width, height) * 0.45f) * 0.1f;
g.drawRect (slider.getLocalBounds().toFloat(), lineThickness);
} else {
drawLinearSliderBackground (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider);
}
}
void drawLinearSliderBackground (Graphics& g, int x, int y, int width, int height,
float /*sliderPos*/,
float /*minSliderPos*/,
float /*maxSliderPos*/,
const Slider::SliderStyle /*style*/, Slider& slider) override
{
const float sliderRadius = getSliderThumbRadius (slider) - 5.0f;
Path on, off;
if (slider.isHorizontal()) {
const float iy = y + height * 0.5f - sliderRadius * 0.5f;
juce::Rectangle<float> r (x - sliderRadius * 0.5f, iy, width + sliderRadius, sliderRadius);
const float onW = r.getWidth() * ((float)slider.valueToProportionOfLength (slider.getValue()));
on.addRectangle (r.removeFromLeft (onW));
off.addRectangle (r);
} else {
const float ix = x + width * 0.5f - sliderRadius * 0.5f;
juce::Rectangle<float> r (ix, y - sliderRadius * 0.5f, sliderRadius, height + sliderRadius);
const float onH = r.getHeight() * ((float)slider.valueToProportionOfLength (slider.getValue()));
on.addRectangle (r.removeFromBottom (onH));
off.addRectangle (r);
}
g.setColour (slider.findColour (Slider::rotarySliderFillColourId));
g.fillPath (on);
g.setColour (slider.findColour (Slider::trackColourId));
g.fillPath (off);
}
void drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
float rotaryStartAngle, float rotaryEndAngle, Slider& slider) override
{
const float radius = jmin(width / 2, height / 2) - 4.0f;
const float centreX = x + width * 0.5f;
const float centreY = y + height * 0.5f;
const float rx = centreX - radius;
const float ry = centreY - radius;
const float rw = radius * 2.0f;
const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled();
//Background
{
g.setColour(Colours::lightgrey.withAlpha (isMouseOver ? 1.0f : 0.7f));
Path intFilledArc;
intFilledArc.addPieSegment(rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, 0.8);
g.fillPath(intFilledArc);
}
if (slider.isEnabled())
g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f));
else
g.setColour (Colour (0x80808080));
//Render knob value
{
Path pathArc;
pathArc.addPieSegment(rx, ry, rw, rw, rotaryStartAngle, angle, 0.8);
g.fillPath(pathArc);
Path cursor, cursorShadow;
float rectWidth = radius*0.4;
float rectHeight = rectWidth/2;
float rectX = centreX + radius*0.9 - rectHeight/2;
float rectY = centreY - rectWidth/2;
cursor.addRectangle (rectX, rectY, rectWidth, rectHeight);
cursorShadow.addRectangle(rectX-1, rectY-1, rectWidth+2, rectHeight+2);
AffineTransform t = AffineTransform::translation(-rectWidth + 2, rectHeight/2);
t = t.rotated((angle - float_Pi/2), centreX, centreY);
cursor.applyTransform(t);
cursorShadow.applyTransform(t);
g.setColour(Colours::black);
g.fillPath(cursor);
g.setColour(Colours::black .withAlpha(0.15f));
g.fillPath(cursorShadow);
}
}
};
/**
* \brief Different kind of slider available
* \see uiSlider
*/
enum SliderType {
HSlider, /*!< Horizontal Slider */
VSlider, /*!< Vertical Slider */
NumEntry, /*!< Numerical Entry Box */
Knob /*!< Circular Slider */
};
/**
* \brief Different kind of VU-meter available.
*/
enum VUMeterType {
HVUMeter, /*!< Horizontal VU-meter */
VVUMeter, /*!< Vertical VU-meter */
Led, /*!< LED VU-meter */
NumDisplay /*!< TextBox VU-meter */
};
/**
* \brief Intern class for all FAUST widgets.
* \details Every active, passive or box widgets derive from this class.
*/
class uiBase
{
protected:
int fTotalWidth, fTotalHeight; // Size with margins included (for a uiBox)
int fDisplayRectWidth, fDisplayRectHeight; // Size without margin, just the child dimensions, sum on one dimension, max on the other
float fHRatio, fVRatio;
public:
/**
* \brief Constructor.
* \details Initialize a uiBase with all its sizes.
*
* \param totWidth Minimal total width.
* \param totHeight Minimal total Height.
*/
uiBase(int totWidth = 0, int totHeight = 0):
fTotalWidth(totWidth), fTotalHeight(totHeight),
fDisplayRectWidth(0), fDisplayRectHeight(0),
fHRatio(1), fVRatio(1)
{}
virtual ~uiBase()
{}
/** Return the size. */
juce::Rectangle<int> getSize()
{
return juce::Rectangle<int>(0, 0, fTotalWidth, fTotalHeight);
}
/** Return the total height in pixels. */
int getTotalHeight()
{
return fTotalHeight;
}
/** Return the total width in pixels. */
int getTotalWidth()
{
return fTotalWidth;
}
/** Return the horizontal ratio, between 0 and 1. */
float getHRatio()
{
return fHRatio;
}
/** Return the vertical ratio, between 0 and 1. */
float getVRatio()
{
return fVRatio;
}
/**
* \brief Set the uiBase bounds.
* \details Convert absolute bounds to relative bounds,
* used in JUCE Component mechanics.
*
* \param r The absolute bounds.
*
*/
void setRelativeSize(Component* comp, const juce::Rectangle<int>& r)
{
comp->setBounds(r.getX() - comp->getParentComponent()->getX(),
r.getY() - comp->getParentComponent()->getY(),
r.getWidth(),
r.getHeight());
}
virtual void init(Component* comp = nullptr)
{
/** Initialize both vertical and horizontal ratios. */
assert(comp);
uiBase* parentBox = comp->findParentComponentOfClass<uiBase>();
if (parentBox != nullptr) {
fHRatio = (float)fTotalWidth / (float)parentBox->fDisplayRectWidth;
fVRatio = (float)fTotalHeight / (float)parentBox->fDisplayRectHeight;
}
}
virtual void setRecommendedSize()
{}
virtual void add(Component* comp)
{}
};
/**
* \brief Intern class for all FAUST active or passive widgets.
* \details Every activ or passive widgets derive from this class.
*/
class uiComponent : public uiBase, public Component, public uiItem
{
public:
/**
* \brief Constructor.
* \details Initialize all uiItem, uiBase and the tooltip variables.
*
* \param gui Current FAUST GUI.
* \param zone Zone of the widget.
* \param w Width of the widget.
* \param h Height of the widget.
* \param name Name of the widget.
*/
uiComponent(GUI* gui, FAUSTFLOAT* zone, int w, int h, String name):uiBase(w, h), Component(name), uiItem(gui, zone)
{}
};
/**
* \brief Intern class for all kind of sliders.
* \see SliderType
*/
class uiSlider : public uiComponent, private juce::Slider::Listener
{
private:
Slider::SliderStyle fStyle;
Label fLabel;
ScopedPointer<ValueConverter> fConverter;
SliderType fType;
Slider fSlider;
public:
/**
* \brief Constructor.
* \details Initialize all uiComponent variables, and Slider specific ones.
* Initialize juce::Slider parameters.
*
* \param gui, zone, w, h, tooltip, name uiComponent variables.
* \param min Minimum value of the slider.
* \param max Maximum value of the slider.
* \param cur Initial value of the slider.
* \param step Step of the slider.
* \param unit Unit of the slider value.
* \param scale Scale of the slider, exponential, logarithmic, or linear.
* \param type Type of slider (see SliderType).
*/
uiSlider(GUI* gui, FAUSTFLOAT* zone, FAUSTFLOAT w, FAUSTFLOAT h, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT cur, FAUSTFLOAT step, String name, String unit, String tooltip, MetaDataUI::Scale scale, SliderType type) : uiComponent(gui, zone, w, h, name), fType(type)
{
if (scale == MetaDataUI::kLog) {
fConverter = new LogValueConverter(min, max, min, max);
fSlider.setSkewFactor(0.5); // Logarithmic slider
} else if (scale == MetaDataUI::kExp) {
fConverter = new ExpValueConverter(min, max, min, max);
fSlider.setSkewFactor(2.0); // Exponential slider
} else {
fConverter = new LinearValueConverter(min, max, min, max);
}
// Set the JUCE widget initalization variables.
switch(fType) {
case HSlider:
fStyle = Slider::SliderStyle::LinearHorizontal;
break;
case VSlider:
fStyle = Slider::SliderStyle::LinearVertical;
fSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 60, 20);
break;
case NumEntry:
fSlider.setIncDecButtonsMode(Slider::incDecButtonsDraggable_AutoDirection);
fStyle = Slider::SliderStyle::IncDecButtons;
break;
case Knob:
fStyle = Slider::SliderStyle::Rotary;
fSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 60, 20);
break;
default:
break;
}
addAndMakeVisible(fSlider);
// Slider settings
fSlider.setRange(min, max, step);
fSlider.setValue(fConverter->faust2ui(cur));
fSlider.addListener(this);
fSlider.setSliderStyle(fStyle);
fSlider.setTextValueSuffix(" " + unit);
fSlider.setTooltip(tooltip);
// Label settings, only happens for a horizontal of numerical entry slider
// because the method attachToComponent only give the choice to place the
// slider name on centered top, which is what we want. It's done manually
// in the paint method.
if (fType == HSlider || fType == NumEntry) {
fLabel.setText(getName(), dontSendNotification);
fLabel.attachToComponent(&fSlider, true);
fLabel.setTooltip(tooltip);
addAndMakeVisible(fLabel);
}
}
/** Draw the name of a vertical or circular slider. */
virtual void paint(Graphics& g) override
{
if (fType == VSlider || fType == Knob) {
g.setColour(Colours::black);
g.drawText(getName(), getLocalBounds(), Justification::centredTop);
}
}
/** Allow to control the slider when its value is changed, but not by the user. */
void reflectZone() override
{
FAUSTFLOAT v = *fZone;
fCache = v;
fSlider.setValue(fConverter->faust2ui(v));
}
/** JUCE callback for a slider value change, give the value to the FAUST module. */
void sliderValueChanged(Slider* slider) override
{
float value = slider->getValue();
//std::cout << getName() << " : " << value << std::endl;
modifyZone(FAUSTFLOAT(value));
}
/**
* Set the good coordinates and size for the juce::Slider object depending
* on its SliderType, whenever the layout size changes.
*/
void resized() override
{
int x, y, width, height;
switch (fType) {
case HSlider: {
int nameWidth = Font().getStringWidth(getName()) + kMargin;
x = nameWidth;
y = 0;
width = getWidth() - nameWidth;
height = getHeight();
break;
}
case VSlider:
x = 0;
y = kNameHeight; // kNameHeight pixels for the name
height = getHeight() - kNameHeight;
width = getWidth();
break;
case NumEntry:
width = kNumEntryWidth;
height = kNumEntryHeight;
// x position is the top left corner horizontal position of the box
// and not the top left of the NumEntry label, so we have to do that
x = (getWidth() - width)/2 + (Font().getStringWidth(getName()) + kMargin)/2;
y = (getHeight() - height)/2;
break;
case Knob:
// The knob name needs to be displayed, kNameHeight pixels
height = width = jmin(getHeight() - kNameHeight, kKnobHeight);
x = (getWidth() - width)/2;
// kNameHeight pixels for the knob name still
y = jmax((getHeight() - height)/2, kNameHeight);
break;
default:
assert(false);
break;
}
fSlider.setBounds(x, y, width, height);
}
};
/** Intern class for button */
class uiButton : public uiComponent, private juce::Button::Listener
{
private:
TextButton fButton;
public:
/**
* \brief Constructor.
* \details Initialize all uiComponent variables and juce::TextButton parameters.
*
* \param gui, zone, w, h, tooltip, label uiComponent variable.
*/
uiButton(GUI* gui, FAUSTFLOAT* zone, FAUSTFLOAT w, FAUSTFLOAT h, String label, String tooltip) : uiComponent(gui, zone, w, h, label)
{
int x = 0;
int y = (getHeight() - kButtonHeight)/2;
fButton.setButtonText(label);
fButton.setBounds(x, y, kButtonWidth, kButtonHeight);
fButton.addListener(this);
fButton.setTooltip(tooltip);
addAndMakeVisible(fButton);
}
/**
* Has to be defined because its a pure virtual function of juce::Button::Listener,
* which uiButton derives from. Control of user actions is done in buttonStateChanged.
* \see buttonStateChanged
*/
void buttonClicked (Button* button) override
{}
/** Indicate to the FAUST module when the button is pressed and released. */
void buttonStateChanged (Button* button) override
{
if (button->isDown()) {
modifyZone(FAUSTFLOAT(1));
} else {
modifyZone(FAUSTFLOAT(0));
}
}
void reflectZone() override
{
FAUSTFLOAT v = *fZone;
fCache = v;
fButton.triggerClick();
}
/** Set the good coordinates and size to the juce::TextButton widget whenever the layout size changes. */
virtual void resized() override
{
int x = 0;
int width = getWidth();
int height = jmin(getHeight(), kButtonHeight);
int y = (getHeight()-height)/2;
fButton.setBounds(x, y, width, height);
}
};
/** Intern class for checkButton */
class uiCheckButton : public uiComponent, private juce::Button::Listener
{
private:
ToggleButton fCheckButton;
public:
/**
* \brief Constructor.
* \details Initialize all uiComponent variables and juce::ToggleButton parameters.
*
* \param gui, zone, w, h, label, tooltip uiComponent variables.
*/
uiCheckButton(GUI* gui, FAUSTFLOAT* zone, FAUSTFLOAT w, FAUSTFLOAT h, String label, String tooltip) : uiComponent(gui, zone, w, h, label)
{
int x = 0;
int y = (getHeight()-h)/2;
fCheckButton.setButtonText(label);
fCheckButton.setBounds(x, y, w, h);
fCheckButton.addListener(this);
fCheckButton.setTooltip(tooltip);
addAndMakeVisible(fCheckButton);
}
/** Indicate to the FAUST module when the button is toggled or not. */
void buttonClicked(Button* button) override
{
//std::cout << getName() << " : " << button->getToggleState() << std::endl;
modifyZone(button->getToggleState());
}
void reflectZone() override
{
FAUSTFLOAT v = *fZone;
fCache = v;
fCheckButton.triggerClick();
}
/** Set the good coordinates and size to the juce::ToggleButton widget, whenever the layout size changes. */
virtual void resized() override
{
fCheckButton.setBounds(getLocalBounds());
}
};
/** Intern class for Menu */
class uiMenu : public uiComponent, private juce::ComboBox::Listener
{
private:
ComboBox fComboBox;
vector<double> fValues;
public:
/**
* \brief Constructor.
* \details Initialize the uiComponent and Menu specific variables, and the juce::ComboBox parameters.
* Menu is considered as a slider in the FAUST logic, with a step of one. The first item
* would be 0 on a slider, the second 1, etc. Each "slider value" is associated with a
* string.
*
* \param gui, zone, w, h, tooltip, label uiComponent variables.
* \param cur Current "slider value" associated with the current item selected.
* \param low Lowest value possible.
* \param hi Highest value possible.
* \param mdescr Menu description. Contains the names of the items associated with their "value".
*/
uiMenu(GUI* gui, FAUSTFLOAT* zone, String label, FAUSTFLOAT w, FAUSTFLOAT h, FAUSTFLOAT cur, FAUSTFLOAT lo, FAUSTFLOAT hi, String tooltip, const char* mdescr) : uiComponent(gui, zone, w, h, label)
{
//Init ComboBox parameters
fComboBox.setEditableText(false);
fComboBox.setJustificationType(Justification::centred);
fComboBox.addListener(this);
addAndMakeVisible(fComboBox);
vector<string> names;
vector<double> values;
if (parseMenuList(mdescr, names, values)) {
int defaultitem = -1;
double mindelta = FLT_MAX;
int item = 1;
// Go through all the Menu's items.
for (int i = 0; i < names.size(); i++) {
double v = values[i];
if ((v >= lo) && (v <= hi)) {
// It is a valid value : add corresponding menu item
// item astrating at 1 because index 0 is reserved for a non-defined item.
fComboBox.addItem(String(names[i].c_str()), item++);
fValues.push_back(v);
// Check if this item is a good candidate to represent the current value
double delta = fabs(cur-v);
if (delta < mindelta) {
mindelta = delta;
defaultitem = fComboBox.getNumItems();
}
}
}
// check the best candidate to represent the current value
if (defaultitem > -1) {
fComboBox.setSelectedItemIndex(defaultitem);
}
}
*fZone = cur;
}
/** Indicate to the FAUST module when the selected items is changed. */
void comboBoxChanged (ComboBox* cb) override
{
//std::cout << getName( )<< " : " << cb->getSelectedId() - 1 << std::endl;
// -1 because of the starting item at 1 at the initialization
modifyZone(fValues[cb->getSelectedId() - 1]);
}
virtual void reflectZone() override
{
FAUSTFLOAT v = *fZone;
fCache = v;
// search closest value
int defaultitem = -1;
double mindelta = FLT_MAX;
for (unsigned int i = 0; i < fValues.size(); i++) {
double delta = fabs(fValues[i]-v);
if (delta < mindelta) {
mindelta = delta;
defaultitem = i;
}
}
if (defaultitem > -1) {
fComboBox.setSelectedItemIndex(defaultitem);
}
}
/** Set the good coordinates and size to the juce::ComboBox widget whenever the layout get reiszed */
virtual void resized() override
{
fComboBox.setBounds(0, 0 + kMenuHeight/2, getWidth(), kMenuHeight/2);
}
/** Display the name of the Menu */
virtual void paint(Graphics& g) override
{
g.setColour(Colours::black);
g.drawText(getName(), getLocalBounds().withHeight(getHeight()/2), Justification::centredTop);
}
};
/** Intern class for RadioButton */
class uiRadioButton : public uiComponent, private juce::Button::Listener
{
private:
bool fIsVertical;
OwnedArray<ToggleButton> fButtons;
vector<double> fValues;
public:
/**
* \brief Constructor.
* \details Initialize the uiComponent variables, and the RadioButton specific variables
* and parameters. Works in a similar way to the Menu, because it is a special
* kind of sliders in the faust logic.
* \see uiMenu
*
* \param gui, zone, tooltip, label uiComponent variables.
* \param w uiComponent variable and width of the RadioButton widget.
* \param h uiComponent variable and height of the RadioButton widget.
* \param cur Current "value" associated with the item selected.
* \param low Lowest "value" possible.
* \param hi Highest "value" possible.
* \param vert True if vertical, false if horizontal.
* \param names Contain the names of the different items.
* \param values Contain the "values" of the different items.
* \param fRadioGroupID RadioButton being multiple CheckButton in JUCE,
* we need an ID to know which are linked together.
*/
uiRadioButton(GUI* gui, FAUSTFLOAT* zone, String label, FAUSTFLOAT w, FAUSTFLOAT h, FAUSTFLOAT cur, FAUSTFLOAT lo, FAUSTFLOAT hi, bool vert, vector<string>& names, vector<double>& values, String tooltip, int radioGroupID) : uiComponent(gui, zone, w, h, label), fIsVertical(vert)
{
ToggleButton* defaultbutton = 0;
double mindelta = FLT_MAX;
for (int i = 0; i < names.size(); i++) {
double v = values[i];
if ((v >= lo) && (v <= hi)) {
// It is a valid value included in slider's range
ToggleButton* tb = new ToggleButton(names[i]);
addAndMakeVisible(tb);
tb->setRadioGroupId (radioGroupID);
tb->addListener(this);
tb->setTooltip(tooltip);
fValues.push_back(v);
fButtons.add(tb);
// Check if this item is a good candidate to represent the current value
double delta = fabs(cur-v);
if (delta < mindelta) {
mindelta = delta;
defaultbutton = tb;
}
}
}
// check the best candidate to represent the current value
if (defaultbutton) {
defaultbutton->setToggleState (true, dontSendNotification);
}
}
virtual void reflectZone() override
{
FAUSTFLOAT v = *fZone;
fCache = v;
// select closest value
int defaultitem = -1;
double mindelta = FLT_MAX;
for (unsigned int i = 0; i < fValues.size(); i++) {
double delta = fabs(fValues[i]-v);
if (delta < mindelta) {
mindelta = delta;
defaultitem = i;
}
}
if (defaultitem > -1) {
fButtons.operator[](defaultitem)->setToggleState (true, dontSendNotification);
}
}
/** Handle the placement of each juce::ToggleButton everytime the layout size is changed. */
virtual void resized() override
{
int width, height;
fIsVertical ? (height = (getHeight() - kNameHeight) / fButtons.size()) : (width = getWidth() / fButtons.size());
for (int i = 0; i < fButtons.size(); i++) {
if (fIsVertical) {
fButtons.operator[](i)->setBounds(0, i * height + kNameHeight, getWidth(), height);
} else {
// kNameHeight pixels offset for the title
fButtons.operator[](i)->setBounds(i * width, kNameHeight, width, getHeight() - kNameHeight);
}
}
}
/** Display the RadioButton name */
virtual void paint(Graphics& g) override
{
g.setColour(Colours::black);
g.drawText(getName(), getLocalBounds().withHeight(kNameHeight), Justification::centredTop);
}
/** Check which button is checked, and give its "value" to the FAUST module */
void buttonClicked(Button* button) override
{
ToggleButton* checkButton = dynamic_cast<ToggleButton*>(button);
//std::cout << getName() << " : " << fButtons.indexOf(checkButton) << std::endl;
modifyZone(fButtons.indexOf(checkButton));
}
};
/**
* \brief Intern class for VU-meter
* \details There is no JUCE widgets for VU-meter, so its fully designed in this class.
*/
class uiVUMeter : public uiComponent, public SettableTooltipClient, public Timer
{
private:
FAUSTFLOAT fLevel; // Current level of the VU-meter.
FAUSTFLOAT fMin, fMax; // Linear range of the VU-meter.
FAUSTFLOAT fScaleMin, fScaleMax; // Range in dB if needed.
bool fDB; // True if it's a dB VU-meter, false otherwise.
VUMeterType fStyle;
String fUnit;
Label fLabel; // Name of the VU-meter.
bool isNameDisplayed()
{
return (!(getName().startsWith("0x")) && getName().isNotEmpty());
}
/** Give the right coordinates and size to the text of Label depending on the VU-meter style */
void setLabelPos()
{
if (fStyle == VVUMeter) {
// -22 on the height because of the text box.
fLabel.setBounds((getWidth()-50)/2, getHeight()-22, 50, 20);
} else if (fStyle == HVUMeter) {
isNameDisplayed() ? fLabel.setBounds(63, (getHeight()-20)/2, 50, 20)
: fLabel.setBounds(3, (getHeight()-20)/2, 50, 20);
} else if (fStyle == NumDisplay) {
fLabel.setBounds((getWidth()-kNumDisplayWidth)/2,
(getHeight()-kNumDisplayHeight/2)/2,
kNumDisplayWidth,
kNumDisplayHeight/2);
}
}
/** Contain all the initialization need for our Label */
void setupLabel(String tooltip)
{
setLabelPos();
fLabel.setEditable(false, false, false);
fLabel.setJustificationType(Justification::centred);
fLabel.setText(String((int)*fZone) + " " + fUnit, dontSendNotification);
fLabel.setTooltip(tooltip);
addAndMakeVisible(fLabel);
}
/**
* \brief Generic method to draw an horizontal VU-meter.
* \details Draw the background of the bargraph, and the TextBox box, without taking
* care of the actual level of the VU-meter
* \see drawHBargraphDB
* \see drawHBargraphLin
*
* \param g JUCE graphics context, used to draw components or images.
* \param width Width of the VU-meter widget.
* \param height Height of the VU-meter widget.
* \param level Current level that needs to be displayed.
* \param dB True if it's a db level, false otherwise.
*/
void drawHBargraph(Graphics& g, int width, int height)
{
float x;
float y = (float)(getHeight()-height)/2;
if (isNameDisplayed()) {
x = 120;
width -= x;
// VUMeter Name
g.setColour(Colours::black);
g.drawText(getName(), 0, y, 60, height, Justification::centredRight);
} else {
x = 60;
width -= x;
}
// VUMeter Background
g.setColour(Colours::lightgrey);
g.fillRect(x, y, (float)width, (float)height);
g.setColour(Colours::black);
g.fillRect(x+1.0f, y+1.0f, (float)width-2, (float)height-2);
// Label Window
g.setColour(Colours::darkgrey);
g.fillRect((int)x-58, (getHeight()-22)/2, 52, 22);
g.setColour(Colours::white.withAlpha(0.8f));
g.fillRect((int)x-57, (getHeight()-20)/2, 50, 20);
// Call the appropriate drawing method for the level.
fDB ? drawHBargraphDB (g, y, height) : drawHBargraphLin(g, x, y, width, height);
}
/**
* Method in charge of drawing the level of a horizontal dB VU-meter.
*
* \param g JUCE graphics context, used to draw components or images.
* \param y y coordinate of the VU-meter.
* \param height Height of the VU-meter.
* \param level Current level of the VU-meter, in dB.
*/
void drawHBargraphDB(Graphics& g, int y, int height)
{
// Drawing Scale
g.setFont(9.0f);
g.setColour(Colours::white);
for (int i = -10; i > fMin; i -= 10) {
paintScale(g, i);
}
for (int i = -6; i < fMax; i += 3) {
paintScale(g, i);
}
int alpha = 200;
FAUSTFLOAT dblevel = dB2Scale(fLevel);
// We need to test here every color changing levels, to avoid to mix colors because of the alpha,
// and so to start the new color rectangle at the end of the previous one.
// Drawing from the minimal range to the current level, or -10dB.
g.setColour(Colour((uint8)40, (uint8)160, (uint8)40, (uint8)alpha));
g.fillRect(dB2x(fMin), y+1.0f, jmin(dB2x(fLevel)-dB2x(fMin), dB2x(-10)-dB2x(fMin)), (float)height-2);
// Drawing from -10dB to the current level, or -6dB.
if (dblevel > dB2Scale(-10)) {
g.setColour(Colour((uint8)160, (uint8)220, (uint8)20, (uint8)alpha));
g.fillRect(dB2x(-10), y+1.0f, jmin(dB2x(fLevel)-dB2x(-10), dB2x(-6)-dB2x(-10)), (float)height-2);
}
// Drawing from -6dB to the current level, or -3dB.
if (dblevel > dB2Scale(-6)) {
g.setColour(Colour((uint8)220, (uint8)220, (uint8)20, (uint8)alpha));
g.fillRect(dB2x(-6), y+1.0f, jmin(dB2x(fLevel)-dB2x(-6), dB2x(-3)-dB2x(-6)), (float)height-2);
}
// Drawing from -3dB to the current level, or 0dB.
if (dblevel > dB2Scale(-3)) {
g.setColour(Colour((uint8)240, (uint8)160, (uint8)20, (uint8)alpha));
g.fillRect(dB2x(-3), y+1.0f, jmin(dB2x(fLevel)-dB2x(-3), dB2x(0)-dB2x(-3)), (float)height-2);
}
// Drawing from 0dB to the current level, or the max range.
if (dblevel > dB2Scale(0)) {
g.setColour(Colour((uint8)240, (uint8)0, (uint8)20, (uint8)alpha));
g.fillRect(dB2x(0), y+1.0f, jmin(dB2x(fLevel)-dB2x(0), dB2x(fMax)-dB2x(0)), (float)height-2);
}
}
/**
* Method in charge of drawing the level of a horizontal linear VU-meter.
*
* \param g JUCE graphics context, used to draw components or images.
* \param x x coordinate of the VU-meter.
* \param y y coordinate of the VU-meter.
* \param height Height of the VU-meter.
* \param width Width of the VU-meter.
* \param level Current level of the VU-meter, in linear logic.
*/
void drawHBargraphLin(Graphics& g, int x, int y, int width, int height)
{
int alpha = 200;
Colour c = juce::Colour((uint8)255, (uint8)165, (uint8)0, (uint8)alpha);
// Drawing from the minimal range to the current level, or 20% of the VU-meter
g.setColour(c.brighter());
g.fillRect(x+1.0f, y+1.0f, jmin<float>(fLevel*(width-2), 0.2f*(width-2)), (float)height-2);
// Drawing from 20% of the VU-meter to the current level, or 90% of the VU-meter
if (fLevel > 0.2f) {
g.setColour(c);
g.fillRect(x+1.0f + 0.2f*(width-2), y+1.0f, jmin<float>((fLevel-0.2f) * (width-2), (0.9f-0.2f) * (width-2)), (float)height-2);
}
// Drawing from 90% of the VU-meter to the current level, or the maximal range of the VU-meter
if (fLevel > 0.9f) {
g.setColour(c.darker());
g.fillRect(x+1.0f + 0.9f*(width-2), y+1.0f, jmin<float>((fLevel-0.9f) * (width-2), (1.0f-0.9f) * (width-2)), (float)height-2);
}
}
/**
* \brief Generic method to draw a vertical VU-meter.
* \details Draw the background of the bargraph, and the TextBox box, without taking
* care of the actual level of the VU-meter
* \see drawHBargraphDB
* \see drawHBargraphLin
*
* \param g JUCE graphics context, used to draw components or images.
* \param width Width of the VU-meter widget.
* \param height Height of the VU-meter widget.
* \param level Current level that needs to be displayed.
* \param dB True if it's a db level, false otherwise.
*/
void drawVBargraph(Graphics& g, int width, int height)
{
float x = (float)(getWidth()-width)/2;
float y;
if (isNameDisplayed()) {
y = (float)getHeight()-height+15;
height -= 40;
// VUMeter Name
g.setColour(Colours::black);
g.drawText(getName(), getLocalBounds(), Justification::centredTop);
} else {
y = (float)getHeight()-height;
height -= 25;
}
// VUMeter Background
g.setColour(Colours::lightgrey);
g.fillRect(x, y, (float)width, (float)height);
g.setColour(Colours::black);
g.fillRect(x+1.0f, y+1.0f, (float)width-2, (float)height-2);
// Label window
g.setColour(Colours::darkgrey);
g.fillRect(jmax((getWidth()-50)/2, 0), getHeight()-23, jmin(getWidth(), 50), 22);
g.setColour(Colours::white.withAlpha(0.8f));
g.fillRect(jmax((getWidth()-48)/2, 1), getHeight()-22, jmin(getWidth()-2, 48), 20);
fDB ? drawVBargraphDB (g, x, width) : drawVBargraphLin(g, x, width);
}
/**
* Method in charge of drawing the level of a vertical dB VU-meter.
*
* \param g JUCE graphics context, used to draw components or images.
* \param x x coordinate of the VU-meter.
* \param width Width of the VU-meter.
* \param level Current level of the VU-meter, in dB.
*/
void drawVBargraphDB(Graphics& g, int x, int width)
{
// Drawing Scale
g.setFont(9.0f);
g.setColour(Colours::white);
for (int i = -10; i > fMin; i -= 10) {
paintScale(g, i);
}
for (int i = -6; i < fMax; i += 3) {
paintScale(g, i);
}
int alpha = 200;
FAUSTFLOAT dblevel = dB2Scale(fLevel);
// We need to test here every color changing levels, to avoid to mix colors because of the alpha,
// and so to start the new color rectangle at the end of the previous one.
// Drawing from the minimal range to the current level, or -10dB.
g.setColour(Colour((uint8)40, (uint8)160, (uint8)40, (uint8)alpha));
g.fillRect(x+1.0f, jmax(dB2y(fLevel), dB2y(-10)), (float)width-2, dB2y(fMin)-jmax(dB2y(fLevel), dB2y(-10)));
// Drawing from -10dB to the current level, or -6dB.
if (dblevel > dB2Scale(-10)) {
g.setColour(Colour((uint8)160, (uint8)220, (uint8)20, (uint8)alpha));
g.fillRect(x+1.0f, jmax(dB2y(fLevel), dB2y(-6)), (float)width-2, dB2y(-10)-jmax(dB2y(fLevel), dB2y(-6)));
}
// Drawing from -6dB to the current level, or -3dB.
if (dblevel > dB2Scale(-6)) {
g.setColour(Colour((uint8)220, (uint8)220, (uint8)20, (uint8)alpha));
g.fillRect(x+1.0f, jmax(dB2y(fLevel), dB2y(-3)), (float)width-2, dB2y(-6)-jmax(dB2y(fLevel), dB2y(-3)));
}
// Drawing from -3dB to the current level, or 0dB.
if (dblevel > dB2Scale(-3)) {
g.setColour(Colour((uint8)240, (uint8)160, (uint8)20, (uint8)alpha));
g.fillRect(x+1.0f, jmax(dB2y(fLevel), dB2y(0)), (float)width-2, dB2y(-3)-jmax(dB2y(fLevel), dB2y(0)));
}
// Drawing from 0dB to the current level, or the maximum range.
if (dblevel > dB2Scale(0)) {
g.setColour(Colour((uint8)240, (uint8)0, (uint8)20, (uint8)alpha));
g.fillRect(x+1.0f, jmax(dB2y(fLevel), dB2y(fMax)), (float)width-2, dB2y(0)-jmax(dB2y(fLevel), dB2y(fMax)));
}
}
/**
* Method in charge of drawing the level of a vertical linear VU-meter.
*
* \param g JUCE graphics context, used to draw components or images.
* \param x x coordinate of the VU-meter.
* \param width Width of the VU-meter.
* \param level Current level of the VU-meter, in linear logic.
*/
void drawVBargraphLin(Graphics& g, int x, int width)
{
int alpha = 200;
Colour c = juce::Colour((uint8)255, (uint8)165, (uint8)0, (uint8)alpha);
// Drawing from the minimal range to the current level, or 20% of the VU-meter.
g.setColour(c.brighter());
g.fillRect(x+1.0f, jmax(lin2y(fLevel), lin2y(0.2)), (float)width-2, lin2y(fMin)-jmax(lin2y(fLevel), lin2y(0.2)));
// Drawing from 20% of the VU-meter to the current level, or 90% of the VU-meter.
if (fLevel > 0.2f) {
g.setColour(c);
g.fillRect(x+1.0f, jmax(lin2y(fLevel), lin2y(0.9)), (float)width-2, lin2y(0.2)-jmax(lin2y(fLevel), lin2y(0.9)));
}
// Drawing from 90% of the VU-meter to the current level, or the maximum range.
if (fLevel > 0.9f) {
g.setColour(c.darker());
g.fillRect(x+1.0f, jmax(lin2y(fLevel), lin2y(fMax)), (float)width-2, lin2y(0.9)-jmax(lin2y(fLevel), lin2y(fMax)));
}
}
/**
* Method in charge of drawing the LED VU-meter, dB or not.
*
* \param g JUCE graphics context, used to draw components or images.
* \param width Width of the LED.
* \param height Height of the LED.
* \param level Current level of the VU-meter, dB or not.
*/
void drawLed(Graphics& g, int width, int height)
{
float x = (float)(getWidth() - width)/2;
float y = (float)(getHeight() - height)/2;
g.setColour(Colours::black);
g.fillEllipse(x, y, width, height);
if (fDB) {
int alpha = 200;
FAUSTFLOAT dblevel = dB2Scale(fLevel);
// Adjust the color depending on the current level
g.setColour(Colour((uint8)40, (uint8)160, (uint8)40, (uint8)alpha));
if (dblevel > dB2Scale(-10)) {
g.setColour(Colour((uint8)160, (uint8)220, (uint8)20, (uint8)alpha));
}
if (dblevel > dB2Scale(-6)) {
g.setColour(Colour((uint8)220, (uint8)220, (uint8)20, (uint8)alpha));
}
if (dblevel > dB2Scale(-3)) {
g.setColour(Colour((uint8)240, (uint8)160, (uint8)20, (uint8)alpha));
}
if (dblevel > dB2Scale(0)) {
g.setColour(Colour((uint8)240, (uint8)0, (uint8)20, (uint8)alpha));
}
g.fillEllipse(x+1, y+1, width-2, height-2);
} else {
// The alpha depend on the level, from 0 to 1
g.setColour(Colours::red.withAlpha((float)fLevel));
g.fillEllipse(x+1, y+1, width-2, height-2);
}
}
/**
* Method in charge of drawing the Numerical Display VU-meter, dB or not.
*
* \param g JUCE graphics context, used to draw components or images.
* \param width Width of the Numerical Display.
* \param height Height of the Numerical Display.
* \param level Current level of the VU-meter.
*/
void drawNumDisplay(Graphics& g, int width, int height)
{
// Centering it
int x = (getWidth()-width) / 2;
int y = (getHeight()-height) / 2;
// Draw box.
g.setColour(Colours::darkgrey);
g.fillRect(x, y, width, height);
g.setColour(Colours::white.withAlpha(0.8f));
g.fillRect(x+1, y+1, width-2, height-2);
// Text is handled by the setLabelPos() function
}
/** Convert a dB level to a y coordinate, for easier draw methods. */
FAUSTFLOAT dB2y(FAUSTFLOAT dB)
{
FAUSTFLOAT s0 = fScaleMin; // Minimal range.
FAUSTFLOAT s1 = fScaleMax; // Maximum range.
FAUSTFLOAT sx = dB2Scale(dB); // Current level.
int h;
int treshold; // Value depend if the name is displayed
if (isNameDisplayed()) {
h = getHeight()-42; // 15 pixels for the VU-Meter name,
// 25 for the textBox, 2 pixels margin.
treshold = 16; // 15 pixels for the VU-Meter name.
} else {
h = getHeight()-27; // 25 for the textBox, 2 pixels margin.
treshold = 1; // 1 pixel margin.
}
return (h - h*(s0-sx)/(s0-s1)) + treshold;
}
/** Convert a linear level to a y coordinate, for easier draw methods. */
FAUSTFLOAT lin2y(FAUSTFLOAT level)
{
int h;
int treshold;
if (isNameDisplayed()) {
h = getHeight()-42; // 15 pixels for the VU-Meter name,
// 25 for the textBox, 2 pixels margin.
treshold = 16; // 15 pixels for the VU-Meter name.
} else {
h = getHeight()-27; // 25 for the textBox, 2 pixels margin.
treshold = 1; // 1 pixel margin.
}
return h * (1 - level) + treshold;
}
/** Convert a dB level to a x coordinate, for easier draw methods. */
FAUSTFLOAT dB2x(FAUSTFLOAT dB)
{
FAUSTFLOAT s0 = fScaleMin; // Minimal range.
FAUSTFLOAT s1 = fScaleMax; // Maximal range.
FAUSTFLOAT sx = dB2Scale(dB); // Current level.
int w;
int treshold;
if (isNameDisplayed()) {
w = getWidth()-122; // 60 pixels for the VU-Meter name,
// 60 for the TextBox, 2 pixels margin.
treshold = 121; // 60 pixels for the VU-Meter name,
// 60 for the TextBox, and 1 pixel margin.
} else {
w = getWidth()-62; // 60 pixels for the TextBox, 2 pixels margin.
treshold = 61; // 60 pixels for the TextBox, 1 pixel margin.
}
return treshold + w - w*(s1-sx)/(s1-s0);
}
/** Write the different level included in the VU-Meter range. */
void paintScale(Graphics& g, float num)
{
juce::Rectangle<int> r;
if (fStyle == VVUMeter) {
r = juce::Rectangle<int>((getWidth()-(kVBargraphWidth/2))/2 + 1, // Left side of the VU-Meter.
dB2y(num), // Vertically centred with 20 height.
(kVBargraphWidth/2)-2, // VU-Meter width with margin.
20); // 20 height.
g.drawText(String(num), r, Justification::centredRight, false);
} else {
r = juce::Rectangle<int>(dB2x(num)-10, // Horizontally centred with 20 width.
(getHeight()-kHBargraphHeight/2)/2 + 1, // Top side of the VU-Meter.
20, // 20 width.
(kHBargraphHeight/2)-2); // VU-Meter height with margin
g.drawText(String(num), r, Justification::centredTop, false);
}
}
/** Set the level, keep it in the range of the VU-Meter, and set the TextBox text. */
void setLevel()
{
FAUSTFLOAT rawLevel = *fZone;
if (fDB) {
fLevel = range(rawLevel);
} else {
fLevel = range((rawLevel-fMin)/(fMax-fMin));
}
fLabel.setText(String((int)rawLevel) + " " + fUnit, dontSendNotification);
}
FAUSTFLOAT range(FAUSTFLOAT level) { return (level > fMax) ? fMax : ((level < fMin) ? fMin : level); }
public:
/**
* \brief Constructor.
* \details Initialize the uiComponent variables and the VU-meter specific ones.
*
* \param gui, zone, w, h, tooltip, label uiComponent variables.
* \param mini Minimal value of the VU-meter range.
* \param maxi Maximal value of the VU-meter range.
* \param unit Unit of the VU-meter (dB or not).
* \param style Type of the VU-meter (see VUMeterType).
* \param vert True if vertical, false if horizontal.
*/
uiVUMeter (GUI* gui, FAUSTFLOAT* zone, FAUSTFLOAT w, FAUSTFLOAT h, String label, FAUSTFLOAT mini, FAUSTFLOAT maxi, String unit, String tooltip, VUMeterType style, bool vert)
: uiComponent(gui, zone, w, h, label), fMin(mini), fMax(maxi), fStyle(style)
{
fLevel = 0; // Initialization of the level
startTimer(50); // Launch a timer that trigger a callback every 50ms
this->fUnit = unit;
fDB = (unit == "dB");
if (fDB) {
// Conversion in dB of the range
fScaleMin = dB2Scale(fMin);
fScaleMax = dB2Scale(fMax);
}
setTooltip(tooltip);
// No text editor for LEDs
if (fStyle != Led) {
setupLabel(tooltip);
}
}
/** Method called by the timer every 50ms, to refresh the VU-meter if it needs to */
void timerCallback() override
{
if (isShowing()) {
//Force painting at the initialisation
bool forceRepaint = (fLevel == 0);
FAUSTFLOAT lastLevel = fLevel; //t-1
setLevel(); //t
// Following condition means that we're repainting our VUMeter only if
// there's one or more changing pixels between last state and this one,
// and if the curent level is included in the VUMeter range. It improves
// performances a lot in IDLE. It's the same for the other style of VUMeter
if (fDB) {
switch (fStyle) {
case VVUMeter:
if (((int)dB2y(lastLevel) != (int)dB2y(fLevel) && fLevel >= fMin && fLevel <= fMax) || forceRepaint) {
repaint();
}
break;
case HVUMeter:
if (((int)dB2x(lastLevel) != (int)dB2x(fLevel) && fLevel >= fMin && fLevel <= fMax) || forceRepaint) {
repaint();
}
break;
case NumDisplay:
if (((int)lastLevel != (int)fLevel && fLevel >= fMin && fLevel <= fMax) || forceRepaint) {
repaint();
}
break;
case Led:
if ((dB2Scale(lastLevel) != dB2Scale(fLevel) && fLevel >= fMin && fLevel <= fMax) || forceRepaint) {
repaint();
}
break;
default:
break;
}
} else {
switch (fStyle) {
case VVUMeter:
if (((int)lin2y(lastLevel) != (int)lin2y(fLevel) && fLevel >= fMin && fLevel <= fMax) || forceRepaint) {
repaint();
}
break;
case HVUMeter:
if ((std::abs(lastLevel-fLevel)>0.01 && fLevel >= fMin && fLevel <= fMax) || forceRepaint) {
repaint();
}
break;
case NumDisplay:
if ((std::abs(lastLevel-fLevel)>0.01 && fLevel >= fMin && fLevel <= fMax) || forceRepaint) {
repaint();
}
break;
case Led:
if (((int)lastLevel != (int)fLevel && fLevel >= fMin && fLevel <= fMax) || forceRepaint) {
repaint();
}
break;
default:
break;
}
}
} else {
fLevel = 0;
}
}
/**
* Call the appropriate drawing method according to the VU-meter style
* \see drawLed
* \see drawNumDisplay
* \see drawVBargraph
* \see drawHBargraph
*/
void paint(Graphics& g) override
{
switch (fStyle) {
case Led:
drawLed(g, kLedWidth, kLedHeight);
break;
case NumDisplay:
drawNumDisplay(g, kNumDisplayWidth, kNumDisplayHeight/2);
break;
case VVUMeter:
drawVBargraph(g, kVBargraphWidth/2, getHeight());
break;
case HVUMeter:
drawHBargraph(g, getWidth(), kHBargraphHeight/2);
break;
default:
break;
}
}
/** Set the Label position whenever the layout size changes. */
void resized() override
{
setLabelPos();
}
void reflectZone() override
{
FAUSTFLOAT v = *fZone;
fCache = v;
}
};
/** Intern class for tab widget */
class uiTabBox : public uiBase, public TabbedComponent
{
public:
/**
* \brief Constructor.
* \details Initalize the juce::TabbedComponent tabs to be at top, and the uiTabBox size at 0
*/
uiTabBox():uiBase(),TabbedComponent(TabbedButtonBar::TabsAtTop)
{}
/**
* Initialize all his child ratios (1 uiBox per tabs), the LookAndFeel
* and the uiTabBox size to fit the biggest of its child.
*/
void init(Component* comp = nullptr) override
{
for (int i = 0; i < getNumTabs(); i++) {
Component* comp = getTabContentComponent(i);
uiBase* base_comp = dynamic_cast<uiBase*>(comp);
base_comp->init(comp);
// The TabbedComponent size should be as big as its bigger child's dimension, done here
fTotalWidth = jmax(fTotalWidth, base_comp->getTotalWidth());
fTotalHeight = jmax(fTotalHeight, base_comp->getTotalHeight());
}
fTotalHeight += 30; // 30 height for the TabBar.
}
void setRecommendedSize() override
{
for (int i = 0; i < getNumTabs(); i++) {
uiBase* comp = dynamic_cast<uiBase*>(getTabContentComponent(i));
comp->setRecommendedSize();
// The TabbedComponent size should be as big as its bigger child's dimension, done here
fTotalWidth = jmax(fTotalWidth, comp->getTotalWidth());
fTotalHeight = jmax(fTotalHeight, comp->getTotalHeight());
}
fTotalHeight += 30; // 30 height for the TabBar
}
void add(Component* comp) override
{
// Name of the component is moved in Tab (so removed from component)
TabbedComponent::addTab(comp->getName(), Colours::white, comp, true);
comp->setName("");
}
};
/**
* \brief Intern class for box widgets
* \details That's the class where the whole layout is calculated.
*/
class uiBox : public uiBase, public Component
{
private:
bool fIsVertical;
bool isNameDisplayed()
{
return (!(getName().startsWith("0x")) && getName().isNotEmpty());
}
/**
* \brief Return the vertical dimension size for a child to be displayed in.
*
*/
int getVSpaceToRemove()
{
// Checking if the name is displayed, to give to good amount space for child components
// kNameHeight pixels is the bix name, kMargin pixel per child components for the margins
if (isNameDisplayed()) {
return (getHeight() - kNameHeight - kMargin * getNumChildComponents());
} else {
return (getHeight() - kMargin * getNumChildComponents());
}
}
/**
* \brief Return the vertical dimension size for a child to be displayed in.
*
*/
int getHSpaceToRemove()
{
// Don't need to check for an horizontal box, as it height doesn't matter
return (getWidth() - kMargin * getNumChildComponents());
}
public:
/**
* \brief Constructor.
* \details Initialize uiBase variables and uiBox specific ones.
*
* \param vert True if it's a vertical box, false otherwise.
* \param boxName Name of the uiBox.
*/
uiBox(bool vert, String boxName): uiBase(0,0), Component(boxName), fIsVertical(vert)
{}
/**
* \brief Destructor.
* \details Delete all uiBox recusively, but not the uiComponent,
* because it's handled by the uiItem FAUST objects.
*/
virtual ~uiBox()
{
/*
Deleting boxes, from leaves to root:
- leaves (uiComponent) are deleted by the uiItem mechanism
- containers (uiBox and uiTabBox) have to be explicitly deleted
*/
for (int i = getNumChildComponents()-1; i >= 0; i--) {
delete dynamic_cast<uiBox*>(getChildComponent(i));
delete dynamic_cast<uiTabBox*>(getChildComponent(i));
}
}
/**
* \brief Initialization of the DisplayRect and Total size.
* \details Calculate the correct size for each box, depending on its child sizes.
*/
void setRecommendedSize() override
{
// Initialized each time
fDisplayRectWidth = fDisplayRectHeight = 0;
// Display rectangle size is the sum of a dimension on a side, and the max of the other one
// on the other side, depending on its orientation (horizontal/vertical).
// Using child's totalSize, because the display rectangle size need to be as big as
// all of its child components with their margins included.
for (int j = 0; j < getNumChildComponents(); j++) {
uiBase* base_comp = dynamic_cast<uiBase*>(getChildComponent(j));
if (fIsVertical) {
fDisplayRectWidth = jmax(fDisplayRectWidth, base_comp->getTotalWidth());
fDisplayRectHeight += base_comp->getTotalHeight();
} else {
fDisplayRectWidth += base_comp->getTotalWidth();
fDisplayRectHeight = jmax(fDisplayRectHeight, base_comp->getTotalHeight());
}
}
fTotalHeight = fDisplayRectHeight;
fTotalWidth = fDisplayRectWidth;
// Adding kMargin pixels of margins per child component on a dimension, and just kMargin on
// the other one, depending on its orientation
if (fIsVertical) {
fTotalHeight += kMargin * getNumChildComponents();
fTotalWidth += kMargin;
} else {
fTotalWidth += kMargin * getNumChildComponents();
fTotalHeight += kMargin;
}
// Adding kNameHeight pixels on its height to allow the name to be displayed
if (isNameDisplayed()) {
fTotalHeight += kNameHeight;
}
}
/** Initiate the current box ratio, and its child's ones recursively. */
void init(Component* comp = nullptr) override
{
uiBase::init(this);
// Going through the Component tree recursively
for (int i = 0; i < getNumChildComponents(); i++) {
Component* comp = getChildComponent(i);
uiBase* base_comp = dynamic_cast<uiBase*>(comp);
base_comp->init(comp);
}
}
/**
* \brief Main layout function.
* \details Allow to place all uiBase child correctly according to their ratios
* and the current box size.
*
* \param displayRect Absolute raw bounds of the current box (with margins
* and space for the title).
*/
void resized() override
{
juce::Rectangle<int> displayRect = getBounds();
// Deleting space for the box name if it needs to be shown
if (isNameDisplayed()) {
displayRect.removeFromTop(kNameHeight);
}
// Putting the margins
displayRect.reduce(kMargin/2, kMargin/2);
// Give child components an adapt size depending on its ratio and the current box size
for (int i = 0; i < getNumChildComponents(); i++) {
Component* comp = getChildComponent(i);
uiBase* base_comp = dynamic_cast<uiBase*>(comp);
if (fIsVertical) {
int heightToRemove = getVSpaceToRemove() * base_comp->getVRatio();
// Remove the space needed from the displayRect, and translate it to show the margins
base_comp->setRelativeSize(comp, displayRect.removeFromTop(heightToRemove).translated(0, kMargin * i));
} else {
int widthToRemove = getHSpaceToRemove() * base_comp->getHRatio();
// Remove the space needed from the displayRect, and translate it to show the margins
base_comp->setRelativeSize(comp, displayRect.removeFromLeft(widthToRemove).translated(kMargin * i, 0));
}
}
}
/**
* Fill the uiBox bounds with a grey color, different shades depending on its order.
* Write the uiBox name if it needs to.
*/
void paint(Graphics& g) override
{
// Fill the box background in gray shades
g.setColour(Colours::black.withAlpha(0.05f));
g.fillRect(getLocalBounds());
// Display the name if it's needed
if (isNameDisplayed()) {
g.setColour(Colours::black);
g.drawText(getName(), getLocalBounds().withHeight(kNameHeight), Justification::centred);
}
}
void add(Component* comp) override
{
addAndMakeVisible(comp);
}
};
/** Class in charge of doing the glue between FAUST and JUCE */
class JuceGUI : public GUI, public MetaDataUI, public Component
{
private:
std::stack<uiBase*> fBoxStack;
uiBase* fCurrentBox = nullptr; // Current box used in buildUserInterface logic.
int fRadioGroupID; // In case of radio buttons.
//ScopedPointer<LookAndFeel> fLaf = new CustomLookAndFeel();
ScopedPointer<LookAndFeel> fLaf = new LookAndFeel_V3();
/** Add generic box to the user interface. */
void openBox(uiBase* box)
{
if (fCurrentBox) {
fCurrentBox->add(dynamic_cast<Component*>(box));
fBoxStack.push(fCurrentBox);
}
fCurrentBox = box;
}
/** Add a slider to the user interface. */
void addSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step, int kWidth, int kHeight, SliderType type)
{
if (isKnob(zone)) {
addKnob(label, zone, init, min, max, step);
} else if (isRadio(zone)) {
addRadioButtons(label, zone, init, min, max, step, fRadioDescription[zone].c_str(), false);
} else if (isMenu(zone)) {
addMenu(label, zone, init, min, max, step, fMenuDescription[zone].c_str());
} else {
fCurrentBox->add(new uiSlider(this, zone, kWidth, kHeight, min, max, init, step, String(label), String(fUnit[zone]), String(fTooltip[zone]), getScale(zone), type));
}
}
/** Add a radio buttons to the user interface. */
void addRadioButtons(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step, const char* mdescr, bool vert)
{
vector<string> names;
vector<double> values;
parseMenuList(mdescr, names, values); // Set names and values vectors
// and not just n checkButtons :
// TODO : check currently unused checkButtonWidth...
int checkButtonWidth = 0;
for (int i = 0; i < names.size(); i++) {
// Checking the maximum of horizontal space needed to display the radio buttons
checkButtonWidth = jmax(Font().getStringWidth(String(names[i])) + 15, checkButtonWidth);
}
if (vert) {
fCurrentBox->add(new uiRadioButton(this, zone, String(label), kCheckButtonWidth, names.size() * (kRadioButtonHeight - 25) + 25, init, min, max, true, names, values, String(fTooltip[zone]), fRadioGroupID++));
} else {
fCurrentBox->add(new uiRadioButton(this, zone, String(label), kCheckButtonWidth, kRadioButtonHeight, init, min, max, false, names, values, String(fTooltip[zone]), fRadioGroupID++));
}
}
/** Add a menu to the user interface. */
void addMenu(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step, const char* mdescr)
{
fCurrentBox->add(new uiMenu(this, zone, String(label), kMenuWidth, kMenuHeight, init, min, max, String(fTooltip[zone]), mdescr));
}
/** Add a ciruclar slider to the user interface. */
void addKnob(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) {
fCurrentBox->add(new uiSlider(this, zone, kKnobWidth, kKnobHeight, min, max, init, step, String(label), String(fUnit[zone]), String(fTooltip[zone]), getScale(zone), Knob));
}
/** Add a bargraph to the user interface. */
void addBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, int kWidth, int kHeight, VUMeterType type)
{
if (isLed(zone)) {
addLed(String(label), zone, min, max);
} else if (isNumerical(zone)) {
addNumericalDisplay(String(label), zone, min, max);
} else {
fCurrentBox->add(new uiVUMeter (this, zone, kWidth, kHeight, String(label), min, max, String(fUnit[zone]), String(fTooltip[zone]), type, false));
}
}
public:
/**
* \brief Constructor.
* \details Initialize the JuceGUI specific variables.
*/
JuceGUI():fRadioGroupID(0)
{}
/**
* \brief Destructor.
* \details Delete root box used in buildUserInterface logic.
*/
~JuceGUI()
{
delete fCurrentBox;
}
/** Return the size of the FAUST program */
juce::Rectangle<int> getSize()
{
// Mininum size in case of empty GUI
if (fCurrentBox) {
juce::Rectangle<int> res = fCurrentBox->getSize();
res.setSize(std::max<int>(1, res.getWidth()), std::max<int>(1, res.getHeight()));
return res;
} else {
return juce::Rectangle<int>(0, 0, 1, 1);
}
}
/** Initialize the uiTabBox component to be visible. */
virtual void openTabBox(const char* label) override
{
openBox(new uiTabBox());
}
/** Add a new vertical box to the user interface. */
virtual void openVerticalBox(const char* label) override
{
openBox(new uiBox(true, String(label)));
}
/** Add a new horizontal box to the user interface. */
virtual void openHorizontalBox(const char* label) override
{
openBox(new uiBox(false, String(label)));
}
/** Close the current box. */
virtual void closeBox() override
{
fCurrentBox->setRecommendedSize();
if (fBoxStack.empty()) {
// Add root box in JuceGUI component
addAndMakeVisible(dynamic_cast<Component*>(fCurrentBox));
fCurrentBox->init();
// Force correct draw
resized();
} else {
fCurrentBox = fBoxStack.top();
fBoxStack.pop();
}
}
/** Add an horizontal slider to the user interface. */
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override
{
addSlider(label, zone, init, min, max, step, kHSliderWidth, kHSliderHeight, HSlider);
}
/** Add a vertical slider to the user interface. */
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override
{
int newWidth = jmax(Font().getStringWidth(String(label)), kVSliderWidth) + kMargin;
addSlider(label, zone, init, min, max, step, newWidth, kVSliderHeight, VSlider);
}
/** Add a button to the user interface. */
virtual void addButton(const char* label, FAUSTFLOAT* zone) override
{
fCurrentBox->add(new uiButton(this, zone, kButtonWidth, kButtonHeight, String(label), String(fTooltip[zone])));
}
/** Add a check button to the user interface. */
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) override
{
// newWidth is his text size, plus the check box size
int newWidth = Font().getStringWidth(String(label)) + kCheckButtonWidth;
fCurrentBox->add(new uiCheckButton(this, zone, newWidth, kCheckButtonHeight, String(label), String(fTooltip[zone])));
}
/** Add a numerical entry to the user interface. */
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override
{
// kMargin pixels between the slider and his name
int newWidth = Font().getStringWidth(String(label)) + kNumEntryWidth + kMargin;
fCurrentBox->add(new uiSlider(this, zone, newWidth, kNumEntryHeight, min, max, init, step, String(label), String(fUnit[zone]), String(fTooltip[zone]), getScale(zone), NumEntry));
}
/** Add a vertical bargraph to the user interface. */
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override
{
addBargraph(label, zone, min, max, kVBargraphWidth, kVBargraphHeight, VVUMeter);
}
/** Add a vertical bargraph to the user interface. */
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override
{
addBargraph(label, zone, min, max, kHBargraphWidth, kHBargraphHeight, HVUMeter);
}
/** Add a LED to the user interface. */
void addLed(String label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
fCurrentBox->add(new uiVUMeter(this, zone, kLedWidth, kLedHeight, label, min, max, String(fUnit[zone]), String(fTooltip[zone]), Led, false));
}
/** Add a numerical display to the user interface. */
void addNumericalDisplay(String label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) {
fCurrentBox->add(new uiVUMeter(this, zone, kNumDisplayWidth, kNumDisplayHeight, label, min, max, String(fUnit[zone]), String(fTooltip[zone]), NumDisplay, false));
}
/** Declare a metadata. */
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* value) override
{
MetaDataUI::declare(zone, key, value);
}
/** Resize its child to match the new bounds */
void resized() override
{
if (fCurrentBox) {
dynamic_cast<Component*>(fCurrentBox)->setBounds(getLocalBounds());
}
}
};
/************************************************************************
FAUST Architecture File
Copyright (C) 2017 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.
************************************************************************
************************************************************************/
#ifndef JuceParameterUI_H
#define JuceParameterUI_H
#include "../JuceLibraryCode/JuceHeader.h"
// Link AudioParameterBool with on/off parameter
struct FaustPlugInAudioParameterBool : public AudioParameterBool, public uiOwnedItem {
FaustPlugInAudioParameterBool(GUI* gui, FAUSTFLOAT* zone, const char* label)
:AudioParameterBool(label, label, false), uiOwnedItem(gui, zone)
{}
void reflectZone() override
{
FAUSTFLOAT v = *fZone;
fCache = v;
setValueNotifyingHost(float(v));
}
virtual void setValue (float newValue) override
{
modifyZone(FAUSTFLOAT(newValue));
}
};
// Link AudioParameterFloat with range parameters
struct FaustPlugInAudioParameterFloat : public AudioParameterFloat, public uiOwnedItem {
FaustPlugInAudioParameterFloat(GUI* gui, FAUSTFLOAT* zone, const char* label, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
:AudioParameterFloat(label, label, float(min), float(max), float(init)), uiOwnedItem(gui, zone)
{}
void reflectZone() override
{
FAUSTFLOAT v = *fZone;
fCache = v;
setValueNotifyingHost(range.convertTo0to1(float(v)));
}
virtual void setValue (float newValue) override
{
modifyZone(FAUSTFLOAT(range.convertFrom0to1(newValue)));
}
};
// A class to create AudioProcessorParameter objects for each zone
class JuceParameterUI : public GUI {
private:
AudioProcessor* fProcessor;
public:
JuceParameterUI(AudioProcessor* processor):fProcessor(processor)
{}
// -- active widgets
virtual void addButton(const char* label, FAUSTFLOAT* zone)
{
fProcessor->addParameter(new FaustPlugInAudioParameterBool(this, zone, label));
}
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
{
fProcessor->addParameter(new FaustPlugInAudioParameterBool(this, zone, label));
}
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
fProcessor->addParameter(new FaustPlugInAudioParameterFloat(this, zone, label, init, min, max, step));
}
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
fProcessor->addParameter(new FaustPlugInAudioParameterFloat(this, zone, label, init, min, max, step));
}
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
fProcessor->addParameter(new FaustPlugInAudioParameterFloat(this, zone, label, init, min, max, step));
}
// -- passive widgets
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
fProcessor->addParameter(new FaustPlugInAudioParameterFloat(this, zone, label, 0, min, max, 0));
}
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
fProcessor->addParameter(new FaustPlugInAudioParameterFloat(this, zone, label, 0, min, max, 0));
}
};
#endif
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2016 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.
************************************************************************
************************************************************************/
#ifndef FAUST_MIDIUI_H
#define FAUST_MIDIUI_H
#ifndef FAUSTFLOAT
#define FAUSTFLOAT float
#endif
#include <vector>
#include <string>
#include <utility>
#include <iostream>
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2011 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.
************************************************************************
************************************************************************/
#ifndef FAUST_JSONUI_H
#define FAUST_JSONUI_H
#ifndef FAUSTFLOAT
#define FAUSTFLOAT float
#endif
#include <vector>
#include <map>
#include <string>
#include <iostream>
#include <sstream>
#include <assert.h>
/*******************************************************************************
* JSONUI : Faust User Interface
* This class produce a complete JSON decription of the DSP instance.
******************************************************************************/
class JSONUI : public PathBuilder, public Meta, public UI
{
protected:
std::stringstream fJSON;
std::stringstream fUI;
std::stringstream fMeta;
std::vector<std::pair <std::string, std::string> > fMetaAux;
std::string fName;
std::string fExpandedCode;
std::string fSHAKey;
char fCloseUIPar;
char fCloseMetaPar;
int fTab;
int fInputs, fOutputs;
void tab(int n, std::ostream& fout)
{
fout << '\n';
while (n-- > 0) {
fout << '\t';
}
}
void addMeta(int tab_val, bool quote = true)
{
if (fMetaAux.size() > 0) {
tab(tab_val, fUI); fUI << "\"meta\": [";
std::string sep = "";
for (size_t i = 0; i < fMetaAux.size(); i++) {
fUI << sep;
tab(tab_val + 1, fUI); fUI << "{ \"" << fMetaAux[i].first << "\": \"" << fMetaAux[i].second << "\" }";
sep = ",";
}
tab(tab_val, fUI); fUI << ((quote) ? "],": "]");
fMetaAux.clear();
}
}
void init(const std::string& name, int inputs, int outputs, const std::string& sha_key, const std::string& dsp_code)
{
fTab = 1;
// Start Meta generation
tab(fTab, fMeta); fMeta << "\"meta\": [";
fCloseMetaPar = ' ';
// Start UI generation
tab(fTab, fUI); fUI << "\"ui\": [";
fCloseUIPar = ' ';
fTab += 1;
fName = name;
fInputs = inputs;
fOutputs = outputs;
fExpandedCode = dsp_code;
fSHAKey = sha_key;
}
inline std::string flatten(const std::string& src)
{
std::stringstream dst;
for (size_t i = 0; i < src.size(); i++) {
switch (src[i]) {
case '\n':
case '\t':
dst << ' ';
break;
case '"':
dst << "\\" << '"';
break;
default:
dst << src[i];
break;
}
}
return dst.str();
}
public:
JSONUI(const std::string& name, int inputs, int outputs, const std::string& sha_key, const std::string& dsp_code)
{
init(name, inputs, outputs, sha_key, dsp_code);
}
JSONUI(const std::string& name, int inputs, int outputs)
{
init(name, inputs, outputs, "", "");
}
JSONUI(int inputs, int outputs)
{
init("", inputs, outputs, "", "");
}
JSONUI()
{
init("", -1, -1, "", "");
}
virtual ~JSONUI() {}
// -- widget's layouts
virtual void openGenericGroup(const char* label, const char* name)
{
fControlsLevel.push_back(label);
fUI << fCloseUIPar;
tab(fTab, fUI); fUI << "{";
fTab += 1;
tab(fTab, fUI); fUI << "\"type\": \"" << name << "\",";
tab(fTab, fUI); fUI << "\"label\": \"" << label << "\",";
addMeta(fTab + 1);
tab(fTab, fUI); fUI << "\"items\": [";
fCloseUIPar = ' ';
fTab += 1;
}
virtual void openTabBox(const char* label)
{
openGenericGroup(label, "tgroup");
}
virtual void openHorizontalBox(const char* label)
{
openGenericGroup(label, "hgroup");
}
virtual void openVerticalBox(const char* label)
{
openGenericGroup(label, "vgroup");
}
virtual void closeBox()
{
fControlsLevel.pop_back();
fTab -= 1;
tab(fTab, fUI); fUI << "]";
fTab -= 1;
tab(fTab, fUI); fUI << "}";
fCloseUIPar = ',';
}
// -- active widgets
virtual void addGenericButton(const char* label, const char* name)
{
fUI << fCloseUIPar;
tab(fTab, fUI); fUI << "{";
tab(fTab + 1, fUI); fUI << "\"type\": \"" << name << "\",";
tab(fTab + 1, fUI); fUI << "\"label\": \"" << label << "\"" << ",";
tab(fTab + 1, fUI); fUI << "\"address\": \"" << buildPath(label) << "\"" << ((fMetaAux.size() > 0) ? "," : "");
addMeta(fTab + 1, false);
tab(fTab, fUI); fUI << "}";
fCloseUIPar = ',';
}
virtual void addButton(const char* label, FAUSTFLOAT* zone)
{
addGenericButton(label, "button");
}
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
{
addGenericButton(label, "checkbox");
}
virtual void addGenericEntry(const char* label, const char* name, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
fUI << fCloseUIPar;
tab(fTab, fUI); fUI << "{";
tab(fTab + 1, fUI); fUI << "\"type\": \"" << name << "\",";
tab(fTab + 1, fUI); fUI << "\"label\": \"" << label << "\"" << ",";
tab(fTab + 1, fUI); fUI << "\"address\": \"" << buildPath(label) << "\"" << ",";
addMeta(fTab + 1);
tab(fTab + 1, fUI); fUI << "\"init\": \"" << init << "\",";
tab(fTab + 1, fUI); fUI << "\"min\": \"" << min << "\",";
tab(fTab + 1, fUI); fUI << "\"max\": \"" << max << "\",";
tab(fTab + 1, fUI); fUI << "\"step\": \"" << step << "\"";
tab(fTab, fUI); fUI << "}";
fCloseUIPar = ',';
}
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addGenericEntry(label, "vslider", init, min, max, step);
}
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addGenericEntry(label, "hslider", init, min, max, step);
}
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addGenericEntry(label, "nentry", init, min, max, step);
}
// -- passive widgets
virtual void addGenericBargraph(const char* label, const char* name, FAUSTFLOAT min, FAUSTFLOAT max)
{
fUI << fCloseUIPar;
tab(fTab, fUI); fUI << "{";
tab(fTab + 1, fUI); fUI << "\"type\": \"" << name << "\",";
tab(fTab + 1, fUI); fUI << "\"label\": \"" << label << "\"" << ",";
tab(fTab + 1, fUI); fUI << "\"address\": \"" << buildPath(label) << "\"" << ",";
addMeta(fTab + 1);
tab(fTab + 1, fUI); fUI << "\"min\": \"" << min << "\",";
tab(fTab + 1, fUI); fUI << "\"max\": \"" << max << "\"";
tab(fTab, fUI); fUI << "}";
fCloseUIPar = ',';
}
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
addGenericBargraph(label, "hbargraph", min, max);
}
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
addGenericBargraph(label, "vbargraph", min, max);
}
// -- metadata declarations
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val)
{
fMetaAux.push_back(std::make_pair(key, val));
}
// Meta interface
virtual void declare(const char* key, const char* value)
{
fMeta << fCloseMetaPar;
if ((strcmp(key, "name") == 0) && (fName == "")) fName = value;
tab(fTab, fMeta); fMeta << "{ " << "\"" << key << "\"" << ": " << "\"" << value << "\" }";
fCloseMetaPar = ',';
}
std::string JSON(bool flat = false)
{
fTab = 0;
fJSON << "{";
fTab += 1;
tab(fTab, fJSON); fJSON << "\"name\": \"" << fName << "\",";
if (fSHAKey != "") { tab(fTab, fJSON); fJSON << "\"sha_key\": \"" << fSHAKey << "\","; }
if (fExpandedCode != "") { tab(fTab, fJSON); fJSON << "\"code\": \"" << fExpandedCode << "\","; }
tab(fTab, fJSON); fJSON << "\"inputs\": \"" << fInputs << "\",";
tab(fTab, fJSON); fJSON << "\"outputs\": \"" << fOutputs << "\",";
tab(fTab, fMeta); fMeta << "],";
tab(fTab, fUI); fUI << "]";
fTab -= 1;
if (fCloseMetaPar == ',') { // If "declare" has been called, fCloseMetaPar state is now ','
fJSON << fMeta.str() << fUI.str();
} else {
fJSON << fUI.str();
}
tab(fTab, fJSON); fJSON << "}" << std::endl;
return (flat) ? flatten(fJSON.str()) : fJSON.str();
}
};
#endif // FAUST_JSONUI_H
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2011 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.
************************************************************************
************************************************************************/
#ifndef __midi__
#define __midi__
#include <vector>
#include <string>
#include <algorithm>
class MapUI;
//----------------------------------------------------------------
// MIDI processor definition
//----------------------------------------------------------------
class midi {
public:
midi() {}
virtual ~midi() {}
// Additional time-stamped API for MIDI input
virtual MapUI* keyOn(double, int channel, int pitch, int velocity)
{
return keyOn(channel, pitch, velocity);
}
virtual void keyOff(double, int channel, int pitch, int velocity = 127)
{
keyOff(channel, pitch, velocity);
}
virtual void pitchWheel(double, int channel, int wheel)
{
pitchWheel(channel, wheel);
}
virtual void ctrlChange(double, int channel, int ctrl, int value)
{
ctrlChange(channel, ctrl, value);
}
virtual void progChange(double, int channel, int pgm)
{
progChange(channel, pgm);
}
virtual void keyPress(double, int channel, int pitch, int press)
{
keyPress(channel, pitch, press);
}
virtual void chanPress(double date, int channel, int press)
{
chanPress(channel, press);
}
virtual void ctrlChange14bits(double, int channel, int ctrl, int value)
{
ctrlChange14bits(channel, ctrl, value);
}
// MIDI sync
virtual void start_sync(double date) {}
virtual void stop_sync(double date) {}
virtual void clock(double date) {}
// Standard MIDI API
virtual MapUI* keyOn(int channel, int pitch, int velocity) { return 0; }
virtual void keyOff(int channel, int pitch, int velocity) {}
virtual void keyPress(int channel, int pitch, int press) {}
virtual void chanPress(int channel, int press) {}
virtual void ctrlChange(int channel, int ctrl, int value) {}
virtual void ctrlChange14bits(int channel, int ctrl, int value) {}
virtual void pitchWheel(int channel, int wheel) {}
virtual void progChange(int channel, int pgm) {}
enum MidiStatus {
// channel voice messages
MIDI_NOTE_OFF = 0x80,
MIDI_NOTE_ON = 0x90,
MIDI_CONTROL_CHANGE = 0xB0,
MIDI_PROGRAM_CHANGE = 0xC0,
MIDI_PITCH_BEND = 0xE0,
MIDI_AFTERTOUCH = 0xD0, // aka channel pressure
MIDI_POLY_AFTERTOUCH = 0xA0, // aka key pressure
MIDI_CLOCK = 0xF8,
MIDI_START = 0xFA,
MIDI_STOP = 0xFC
};
enum MidiCtrl {
ALL_NOTES_OFF = 123,
ALL_SOUND_OFF = 120
};
};
//----------------------------------------------------------------
// Base class for MIDI API handling
//----------------------------------------------------------------
class midi_handler : public midi {
protected:
std::vector<midi*> fMidiInputs;
std::string fName;
public:
midi_handler(const std::string& name = "MIDIHandler"):fName(name) {}
virtual ~midi_handler() {}
virtual void addMidiIn(midi* midi_dsp) { fMidiInputs.push_back(midi_dsp); }
virtual void removeMidiIn(midi* midi_dsp)
{
std::vector<midi*>::iterator it = std::find(fMidiInputs.begin(), fMidiInputs.end(), midi_dsp);
if (it != fMidiInputs.end()) {
fMidiInputs.erase(it);
}
}
virtual bool start_midi() { return true; }
virtual void stop_midi() {}
void handleSync(double time, int type)
{
if (type == MIDI_CLOCK) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->clock(time);
}
} else if (type == MIDI_START) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->start_sync(time);
}
} else if (type == MIDI_STOP) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->stop_sync(time);
}
}
}
void handleData1(double time, int type, int channel, int data1)
{
if (type == MIDI_PROGRAM_CHANGE) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->progChange(time, channel, data1);
}
} else if (type == MIDI_AFTERTOUCH) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->chanPress(time, channel, data1);
}
}
}
void handleData2(double time, int type, int channel, int data1, int data2)
{
if (type == MIDI_NOTE_OFF || ((type == MIDI_NOTE_ON) && (data2 == 0))) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->keyOff(time, channel, data1, data2);
}
} else if (type == MIDI_NOTE_ON) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->keyOn(time, channel, data1, data2);
}
} else if (type == MIDI_CONTROL_CHANGE) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->ctrlChange(time, channel, data1, data2);
}
} else if (type == MIDI_PITCH_BEND) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->pitchWheel(time, channel, (data2 * 128.0) + data1);
}
} else if (type == MIDI_POLY_AFTERTOUCH) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->keyPress(time, channel, data1, data2);
}
}
}
};
#endif // __midi__
#ifdef _MSC_VER
#define gsscanf sscanf_s
#else
#define gsscanf sscanf
#endif
/*****************************************************************************
* Helper code for MIDI meta and polyphonic 'nvoices' parsing
******************************************************************************/
struct MidiMeta : public Meta, public std::map<std::string, std::string>
{
void declare(const char* key, const char* value)
{
(*this)[key] = value;
}
const std::string get(const char* key, const char* def)
{
if (this->find(key) != this->end()) {
return (*this)[key];
} else {
return def;
}
}
static void analyse(dsp* tmp_dsp, bool& midi_sync, int& nvoices)
{
JSONUI jsonui;
tmp_dsp->buildUserInterface(&jsonui);
std::string json = jsonui.JSON();
midi_sync = ((json.find("midi") != std::string::npos) &&
((json.find("start") != std::string::npos) ||
(json.find("stop") != std::string::npos) ||
(json.find("clock") != std::string::npos)));
#if defined(NVOICES) && NVOICES!=NUM_VOICES
nvoices = NVOICES;
#else
MidiMeta meta;
tmp_dsp->metadata(&meta);
std::string numVoices = meta.get("nvoices", "0");
nvoices = atoi(numVoices.c_str());
if (nvoices < 0) nvoices = 0;
#endif
}
};
/*******************************************************************************
* MidiUI : Faust User Interface
* This class decodes MIDI meta data and maps incoming MIDI messages to them.
* Currently ctrl, keyon/keyoff, keypress, pgm, chanpress, pitchwheel/pitchbend
* start/stop/clock meta data is handled.
******************************************************************************/
class uiMidiItem : public uiItem {
protected:
midi* fMidiOut;
bool fInputCtrl;
public:
uiMidiItem(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input)
:uiItem(ui, zone), fMidiOut(midi_out), fInputCtrl(input) {}
virtual ~uiMidiItem() {}
};
class uiMidiTimedItem : public uiMidiItem
{
protected:
bool fDelete;
public:
uiMidiTimedItem(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input = true)
:uiMidiItem(midi_out, ui, zone, input)
{
if (GUI::gTimedZoneMap.find(fZone) == GUI::gTimedZoneMap.end()) {
GUI::gTimedZoneMap[fZone] = ringbuffer_create(8192);
fDelete = true;
} else {
fDelete = false;
}
}
virtual ~uiMidiTimedItem()
{
ztimedmap::iterator it;
if (fDelete && ((it = GUI::gTimedZoneMap.find(fZone)) != GUI::gTimedZoneMap.end())) {
ringbuffer_free((*it).second);
GUI::gTimedZoneMap.erase(it);
}
}
void modifyZone(double date, FAUSTFLOAT v)
{
size_t res;
DatedControl dated_val(date, v);
if ((res = ringbuffer_write(GUI::gTimedZoneMap[fZone], (const char*)&dated_val, sizeof(DatedControl))) != sizeof(DatedControl)) {
std::cerr << "ringbuffer_write error DatedControl" << std::endl;
}
}
// TODO
virtual void reflectZone() {}
};
// MIDI sync
class uiMidiStart : public uiMidiTimedItem
{
public:
uiMidiStart(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input = true)
:uiMidiTimedItem(midi_out, ui, zone, input)
{}
virtual ~uiMidiStart()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
if (v != FAUSTFLOAT(0)) {
fMidiOut->start_sync(0);
}
}
};
class uiMidiStop : public uiMidiTimedItem
{
public:
uiMidiStop(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input = true)
:uiMidiTimedItem(midi_out, ui, zone, input)
{}
virtual ~uiMidiStop()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
if (v != FAUSTFLOAT(1)) {
fMidiOut->stop_sync(0);
}
}
};
class uiMidiClock : public uiMidiTimedItem
{
private:
bool fState;
public:
uiMidiClock(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input = true)
:uiMidiTimedItem(midi_out, ui, zone, input), fState(false)
{}
virtual ~uiMidiClock()
{}
void modifyZone(double date, FAUSTFLOAT v)
{
if (fInputCtrl) {
fState = !fState;
uiMidiTimedItem::modifyZone(date, FAUSTFLOAT(fState));
}
}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
fMidiOut->clock(0);
}
};
class uiMidiProgChange : public uiMidiItem
{
private:
int fPgm;
public:
uiMidiProgChange(midi* midi_out, int pgm, GUI* ui, FAUSTFLOAT* zone, bool input = true)
:uiMidiItem(midi_out, ui, zone, input), fPgm(pgm)
{}
virtual ~uiMidiProgChange()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
if (v != FAUSTFLOAT(0)) {
fMidiOut->progChange(0, fPgm);
}
}
};
class uiMidiChanPress : public uiMidiItem
{
private:
int fPress;
public:
uiMidiChanPress(midi* midi_out, int press, GUI* ui, FAUSTFLOAT* zone, bool input = true)
:uiMidiItem(midi_out, ui, zone, input), fPress(press)
{}
virtual ~uiMidiChanPress()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
if (v != FAUSTFLOAT(0)) {
fMidiOut->chanPress(0, fPress);
}
}
};
class uiMidiCtrlChange : public uiMidiItem
{
private:
int fCtrl;
LinearValueConverter fConverter;
public:
uiMidiCtrlChange(midi* midi_out, int ctrl, GUI* ui, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true)
:uiMidiItem(midi_out, ui, zone, input), fCtrl(ctrl), fConverter(0., 127., double(min), double(max))
{}
virtual ~uiMidiCtrlChange()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
fMidiOut->ctrlChange(0, fCtrl, fConverter.faust2ui(v));
}
void modifyZone(int v)
{
if (fInputCtrl) {
uiItem::modifyZone(FAUSTFLOAT(fConverter.ui2faust(v)));
}
}
};
class uiMidiPitchWheel : public uiMidiItem
{
private:
// currently, the range is of pitchwheel if fixed (-2/2 semitones)
FAUSTFLOAT wheel2bend(float v)
{
return pow(2.0,(v/16383.0*4-2)/12);
}
int bend2wheel(float v)
{
return (int)((12*log(v)/log(2)+2)/4*16383);
}
public:
uiMidiPitchWheel(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input = true)
:uiMidiItem(midi_out, ui, zone, input)
{}
virtual ~uiMidiPitchWheel()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
fMidiOut->pitchWheel(0, bend2wheel(v));
}
void modifyZone(int v)
{
if (fInputCtrl) {
uiItem::modifyZone(wheel2bend(v));
}
}
};
class uiMidiKeyOn : public uiMidiItem
{
private:
int fKeyOn;
LinearValueConverter fConverter;
public:
uiMidiKeyOn(midi* midi_out, int key, GUI* ui, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true)
:uiMidiItem(midi_out, ui, zone, input), fKeyOn(key), fConverter(0., 127., double(min), double(max))
{}
virtual ~uiMidiKeyOn()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
fMidiOut->keyOn(0, fKeyOn, fConverter.faust2ui(v));
}
void modifyZone(int v)
{
if (fInputCtrl) {
uiItem::modifyZone(FAUSTFLOAT(fConverter.ui2faust(v)));
}
}
};
class uiMidiKeyOff : public uiMidiItem
{
private:
int fKeyOff;
LinearValueConverter fConverter;
public:
uiMidiKeyOff(midi* midi_out, int key, GUI* ui, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true)
:uiMidiItem(midi_out, ui, zone, input), fKeyOff(key), fConverter(0., 127., double(min), double(max))
{}
virtual ~uiMidiKeyOff()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
fMidiOut->keyOff(0, fKeyOff, fConverter.faust2ui(v));
}
void modifyZone(int v)
{
if (fInputCtrl) {
uiItem::modifyZone(FAUSTFLOAT(fConverter.ui2faust(v)));
}
}
};
class uiMidiKeyPress : public uiMidiItem
{
private:
int fKeyOn;
LinearValueConverter fConverter;
public:
uiMidiKeyPress(midi* midi_out, int key, GUI* ui, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true)
:uiMidiItem(midi_out, ui, zone, input), fKeyOn(key), fConverter(0., 127., double(min), double(max))
{}
virtual ~uiMidiKeyPress()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
fMidiOut->keyPress(0, fKeyOn, fConverter.faust2ui(v));
}
void modifyZone(int v)
{
if (fInputCtrl) {
uiItem::modifyZone(FAUSTFLOAT(fConverter.ui2faust(v)));
}
}
};
class MapUI;
class MidiUI : public GUI, public midi
{
protected:
std::map <int, std::vector<uiMidiCtrlChange*> > fCtrlChangeTable;
std::map <int, std::vector<uiMidiProgChange*> > fProgChangeTable;
std::map <int, std::vector<uiMidiChanPress*> > fChanPressTable;
std::map <int, std::vector<uiMidiKeyOn*> > fKeyOnTable;
std::map <int, std::vector<uiMidiKeyOff*> > fKeyOffTable;
std::map <int, std::vector<uiMidiKeyPress*> > fKeyPressTable;
std::vector<uiMidiPitchWheel*> fPitchWheelTable;
std::vector<uiMidiStart*> fStartTable;
std::vector<uiMidiStop*> fStopTable;
std::vector<uiMidiClock*> fClockTable;
std::vector<std::pair <std::string, std::string> > fMetaAux;
midi_handler* fMidiHandler;
void addGenericZone(FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true)
{
if (fMetaAux.size() > 0) {
for (size_t i = 0; i < fMetaAux.size(); i++) {
unsigned num;
if (fMetaAux[i].first == "midi") {
if (gsscanf(fMetaAux[i].second.c_str(), "ctrl %u", &num) == 1) {
fCtrlChangeTable[num].push_back(new uiMidiCtrlChange(fMidiHandler, num, this, zone, min, max, input));
} else if (gsscanf(fMetaAux[i].second.c_str(), "keyon %u", &num) == 1) {
fKeyOnTable[num].push_back(new uiMidiKeyOn(fMidiHandler, num, this, zone, min, max, input));
} else if (gsscanf(fMetaAux[i].second.c_str(), "keyoff %u", &num) == 1) {
fKeyOffTable[num].push_back(new uiMidiKeyOff(fMidiHandler, num, this, zone, min, max, input));
} else if (gsscanf(fMetaAux[i].second.c_str(), "keypress %u", &num) == 1) {
fKeyPressTable[num].push_back(new uiMidiKeyPress(fMidiHandler, num, this, zone, min, max, input));
} else if (gsscanf(fMetaAux[i].second.c_str(), "pgm %u", &num) == 1) {
fProgChangeTable[num].push_back(new uiMidiProgChange(fMidiHandler, num, this, zone, input));
} else if (gsscanf(fMetaAux[i].second.c_str(), "chanpress %u", &num) == 1) {
fChanPressTable[num].push_back(new uiMidiChanPress(fMidiHandler, num, this, zone, input));
} else if (strcmp(fMetaAux[i].second.c_str(), "pitchwheel") == 0
|| strcmp(fMetaAux[i].second.c_str(), "pitchbend") == 0) {
fPitchWheelTable.push_back(new uiMidiPitchWheel(fMidiHandler, this, zone, input));
// MIDI sync
} else if (strcmp(fMetaAux[i].second.c_str(), "start") == 0) {
fStartTable.push_back(new uiMidiStart(fMidiHandler, this, zone, input));
} else if (strcmp(fMetaAux[i].second.c_str(), "stop") == 0) {
fStopTable.push_back(new uiMidiStop(fMidiHandler, this, zone, input));
} else if (strcmp(fMetaAux[i].second.c_str(), "clock") == 0) {
fClockTable.push_back(new uiMidiClock(fMidiHandler, this, zone, input));
}
}
}
}
fMetaAux.clear();
}
public:
MidiUI(midi_handler* midi_handler)
{
fMidiHandler = midi_handler;
fMidiHandler->addMidiIn(this);
}
virtual ~MidiUI()
{
fMidiHandler->removeMidiIn(this);
}
bool run() { return fMidiHandler->start_midi(); }
void stop() { fMidiHandler->stop_midi(); }
void addMidiIn(midi* midi_dsp) { fMidiHandler->addMidiIn(midi_dsp); }
void removeMidiIn(midi* midi_dsp) { fMidiHandler->removeMidiIn(midi_dsp); }
// -- active widgets
virtual void addButton(const char* label, FAUSTFLOAT* zone)
{
addGenericZone(zone, FAUSTFLOAT(0), FAUSTFLOAT(1));
}
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
{
addGenericZone(zone, FAUSTFLOAT(0), FAUSTFLOAT(1));
}
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addGenericZone(zone, min, max);
}
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addGenericZone(zone, min, max);
}
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addGenericZone(zone, min, max);
}
// -- passive widgets
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
addGenericZone(zone, min, max, false);
}
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
addGenericZone(zone, min, max, false);
}
// -- metadata declarations
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val)
{
fMetaAux.push_back(std::make_pair(key, val));
}
// -- MIDI API
MapUI* keyOn(double date, int channel, int note, int velocity)
{
if (fKeyOnTable.find(note) != fKeyOnTable.end()) {
for (unsigned int i = 0; i < fKeyOnTable[note].size(); i++) {
fKeyOnTable[note][i]->modifyZone(FAUSTFLOAT(velocity));
}
}
return 0;
}
void keyOff(double date, int channel, int note, int velocity)
{
if (fKeyOffTable.find(note) != fKeyOffTable.end()) {
for (unsigned int i = 0; i < fKeyOffTable[note].size(); i++) {
fKeyOffTable[note][i]->modifyZone(FAUSTFLOAT(velocity));
}
}
}
void ctrlChange(double date, int channel, int ctrl, int value)
{
if (fCtrlChangeTable.find(ctrl) != fCtrlChangeTable.end()) {
for (unsigned int i = 0; i < fCtrlChangeTable[ctrl].size(); i++) {
fCtrlChangeTable[ctrl][i]->modifyZone(FAUSTFLOAT(value));
}
}
}
void progChange(double date, int channel, int pgm)
{
if (fProgChangeTable.find(pgm) != fProgChangeTable.end()) {
for (unsigned int i = 0; i < fProgChangeTable[pgm].size(); i++) {
fProgChangeTable[pgm][i]->modifyZone(FAUSTFLOAT(1));
}
}
}
void pitchWheel(double date, int channel, int wheel)
{
for (unsigned int i = 0; i < fPitchWheelTable.size(); i++) {
fPitchWheelTable[i]->modifyZone(FAUSTFLOAT(wheel));
}
}
void keyPress(double date, int channel, int pitch, int press)
{
if (fKeyPressTable.find(press) != fKeyPressTable.end()) {
for (unsigned int i = 0; i < fKeyPressTable[press].size(); i++) {
fKeyPressTable[press][i]->modifyZone(FAUSTFLOAT(press));
}
}
}
void chanPress(double date, int channel, int press)
{
if (fChanPressTable.find(press) != fChanPressTable.end()) {
for (unsigned int i = 0; i < fChanPressTable[press].size(); i++) {
fChanPressTable[press][i]->modifyZone(FAUSTFLOAT(1));
}
}
}
void ctrlChange14bits(double date, int channel, int ctrl, int value) {}
// MIDI sync
void start_sync(double date)
{
for (unsigned int i = 0; i < fStartTable.size(); i++) {
fStartTable[i]->modifyZone(date, FAUSTFLOAT(1));
}
}
void stop_sync(double date)
{
for (unsigned int i = 0; i < fStopTable.size(); i++) {
fStopTable[i]->modifyZone(date, FAUSTFLOAT(0));
}
}
void clock(double date)
{
for (unsigned int i = 0; i < fClockTable.size(); i++) {
fClockTable[i]->modifyZone(date, FAUSTFLOAT(1));
}
}
};
#endif // FAUST_MIDIUI_H
#include <math.h>
#include <algorithm>
/************************************************************************
FAUST Architecture File
Copyright (C) 2017 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.
************************************************************************
************************************************************************/
#ifndef JuceStateUI_H
#define JuceStateUI_H
#include "../JuceLibraryCode/JuceHeader.h"
#include <vector>
// A class to save/restore DSP state using JUCE
class JuceStateUI : public BaseUI {
private:
std::vector<FAUSTFLOAT*> fZones;
public:
void getStateInformation (MemoryBlock& destData)
{
MemoryOutputStream stream (destData, true);
if (sizeof(FAUSTFLOAT) == sizeof(float)) {
for (int i = 0; i < fZones.size(); i++) {
stream.writeFloat(*fZones[i]);
}
} else {
for (int i = 0; i < fZones.size(); i++) {
stream.writeDouble(*fZones[i]);
}
}
}
void setStateInformation (const void* data, int sizeInBytes)
{
MemoryInputStream stream (data, static_cast<size_t> (sizeInBytes), false);
if (sizeof(FAUSTFLOAT) == sizeof(float)) {
for (int i = 0; i < sizeInBytes / sizeof(float); i++) {
*fZones[i] = stream.readFloat();
}
} else {
for (int i = 0; i < sizeInBytes / sizeof(double); i++) {
*fZones[i] = stream.readDouble();
}
}
}
// -- active widgets
virtual void addButton(const char* label, FAUSTFLOAT* zone) { fZones.push_back(zone); }
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) { fZones.push_back(zone); }
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) { fZones.push_back(zone); }
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) { fZones.push_back(zone); }
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) { fZones.push_back(zone); };
};
#endif
#if defined(OSCCTRL)
/************************************************************************
FAUST Architecture File
Copyright (C) 2016 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.
************************************************************************
************************************************************************/
#ifndef __juce_osc__
#define __juce_osc__
#include "../JuceLibraryCode/JuceHeader.h"
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2016 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.
************************************************************************
************************************************************************/
#ifndef API_UI_H
#define API_UI_H
#include <sstream>
#include <string>
#include <vector>
#include <iostream>
#include <map>
class APIUI : public PathBuilder, public Meta, public UI
{
protected:
enum { kLin = 0, kLog = 1, kExp = 2 };
int fNumParameters;
std::vector<std::string> fName;
std::map<std::string, int> fPathMap;
std::map<std::string, int> fLabelMap;
std::vector<ValueConverter*> fConversion;
std::vector<FAUSTFLOAT*> fZone;
std::vector<FAUSTFLOAT> fInit;
std::vector<FAUSTFLOAT> fMin;
std::vector<FAUSTFLOAT> fMax;
std::vector<FAUSTFLOAT> fStep;
std::vector<std::string> fUnit;
std::vector<std::string> fTooltip;
std::vector<ZoneControl*> fAcc[3];
std::vector<ZoneControl*> fGyr[3];
// Screen color control
// "...[screencolor:red]..." etc.
bool fHasScreenControl; // true if control screen color metadata
ZoneReader* fRedReader;
ZoneReader* fGreenReader;
ZoneReader* fBlueReader;
// Current values controlled by metadata
std::string fCurrentUnit;
int fCurrentScale;
std::string fCurrentAcc;
std::string fCurrentGyr;
std::string fCurrentColor;
std::string fCurrentTooltip;
// Add a generic parameter
virtual void addParameter(const char* label,
FAUSTFLOAT* zone,
FAUSTFLOAT init,
FAUSTFLOAT min,
FAUSTFLOAT max,
FAUSTFLOAT step)
{
std::string path = buildPath(label);
fPathMap[path] = fLabelMap[label] = fNumParameters++;
fName.push_back(path);
fZone.push_back(zone);
fInit.push_back(init);
fMin.push_back(min);
fMax.push_back(max);
fStep.push_back(step);
//handle unit metadata
fUnit.push_back(fCurrentUnit);
fCurrentUnit = "";
//handle tooltip metadata
fTooltip.push_back(fCurrentTooltip);
fCurrentTooltip = "";
//handle scale metadata
switch (fCurrentScale) {
case kLin : fConversion.push_back(new LinearValueConverter(0, 1, min, max)); break;
case kLog : fConversion.push_back(new LogValueConverter(0, 1, min, max)); break;
case kExp : fConversion.push_back(new ExpValueConverter(0, 1, min, max)); break;
}
fCurrentScale = kLin;
if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) {
std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n";
}
// handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..."
if (fCurrentAcc.size() > 0) {
std::istringstream iss(fCurrentAcc);
int axe, curve;
double amin, amid, amax;
iss >> axe >> curve >> amin >> amid >> amax;
if ((0 <= axe) && (axe < 3) &&
(0 <= curve) && (curve < 4) &&
(amin < amax) && (amin <= amid) && (amid <= amax))
{
fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
} else {
std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl;
}
fCurrentAcc = "";
}
// handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..."
if (fCurrentGyr.size() > 0) {
std::istringstream iss(fCurrentGyr);
int axe, curve;
double amin, amid, amax;
iss >> axe >> curve >> amin >> amid >> amax;
if ((0 <= axe) && (axe < 3) &&
(0 <= curve) && (curve < 4) &&
(amin < amax) && (amin <= amid) && (amid <= amax))
{
fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max));
} else {
std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl;
}
fCurrentGyr = "";
}
// handle screencolor metadata "...[screencolor:red|green|blue]..."
if (fCurrentColor.size() > 0) {
if ((fCurrentColor == "red") && (fRedReader == 0)) {
fRedReader = new ZoneReader(zone, min, max);
fHasScreenControl = true;
} else if ((fCurrentColor == "green") && (fGreenReader == 0)) {
fGreenReader = new ZoneReader(zone, min, max);
fHasScreenControl = true;
} else if ((fCurrentColor == "blue") && (fBlueReader == 0)) {
fBlueReader = new ZoneReader(zone, min, max);
fHasScreenControl = true;
} else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) {
fRedReader = new ZoneReader(zone, min, max);
fGreenReader = new ZoneReader(zone, min, max);
fBlueReader = new ZoneReader(zone, min, max);
fHasScreenControl = true;
} else {
std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl;
}
}
fCurrentColor = "";
}
int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val)
{
FAUSTFLOAT* zone = fZone[p];
for (int i = 0; i < table[val].size(); i++) {
if (zone == table[val][i]->getZone()) return i;
}
return -1;
}
void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax)
{
int id1 = getZoneIndex(table, p, 0);
int id2 = getZoneIndex(table, p, 1);
int id3 = getZoneIndex(table, p, 2);
// Deactivates everywhere..
if (id1 != -1) table[0][id1]->setActive(false);
if (id2 != -1) table[1][id2]->setActive(false);
if (id3 != -1) table[2][id3]->setActive(false);
if (val == -1) { // Means: no more mapping...
// So stay all deactivated...
} else {
int id4 = getZoneIndex(table, p, val);
if (id4 != -1) {
// Reactivate the one we edit...
table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]);
table[val][id4]->setActive(true);
} else {
// Allocate a new CurveZoneControl which is 'active' by default
FAUSTFLOAT* zone = fZone[p];
table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]));
}
}
}
void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax)
{
int id1 = getZoneIndex(table, p, 0);
int id2 = getZoneIndex(table, p, 1);
int id3 = getZoneIndex(table, p, 2);
if (id1 != -1) {
val = 0;
curve = table[val][id1]->getCurve();
table[val][id1]->getMappingValues(amin, amid, amax);
} else if (id2 != -1) {
val = 1;
curve = table[val][id2]->getCurve();
table[val][id2]->getMappingValues(amin, amid, amax);
} else if (id3 != -1) {
val = 2;
curve = table[val][id3]->getCurve();
table[val][id3]->getMappingValues(amin, amid, amax);
} else {
val = -1; // No mapping
curve = 0;
amin = -100.;
amid = 0.;
amax = 100.;
}
}
public:
enum Type { kAcc = 0, kGyr = 1, kNoType };
APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0)
{}
virtual ~APIUI()
{
std::vector<ValueConverter*>::iterator it1;
for (it1 = fConversion.begin(); it1 != fConversion.end(); it1++) {
delete(*it1);
}
std::vector<ZoneControl*>::iterator it2;
for (int i = 0; i < 3; i++) {
for (it2 = fAcc[i].begin(); it2 != fAcc[i].end(); it2++) {
delete(*it2);
}
for (it2 = fGyr[i].begin(); it2 != fGyr[i].end(); it2++) {
delete(*it2);
}
}
delete fRedReader;
delete fGreenReader;
delete fBlueReader;
}
// -- widget's layouts
virtual void openTabBox(const char* label) { fControlsLevel.push_back(label); }
virtual void openHorizontalBox(const char* label) { fControlsLevel.push_back(label); }
virtual void openVerticalBox(const char* label) { fControlsLevel.push_back(label); }
virtual void closeBox() { fControlsLevel.pop_back(); }
// -- active widgets
virtual void addButton(const char* label, FAUSTFLOAT* zone)
{
addParameter(label, zone, 0, 0, 1, 1);
}
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
{
addParameter(label, zone, 0, 0, 1, 1);
}
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addParameter(label, zone, init, min, max, step);
}
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addParameter(label, zone, init, min, max, step);
}
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
{
addParameter(label, zone, init, min, max, step);
}
// -- passive widgets
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
addParameter(label, zone, min, min, max, (max-min)/1000.0);
}
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max)
{
addParameter(label, zone, min, min, max, (max-min)/1000.0);
}
// -- metadata declarations
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val)
{
if (strcmp(key, "scale") == 0) {
if (strcmp(val, "log") == 0) {
fCurrentScale = kLog;
} else if (strcmp(val, "exp") == 0) {
fCurrentScale = kExp;
} else {
fCurrentScale = kLin;
}
} else if (strcmp(key, "unit") == 0) {
fCurrentUnit = val;
} else if (strcmp(key, "acc") == 0) {
fCurrentAcc = val;
} else if (strcmp(key, "gyr") == 0) {
fCurrentGyr = val;
} else if (strcmp(key, "screencolor") == 0) {
fCurrentColor = val; // val = "red", "green" or "blue"
} else if (strcmp(key, "tooltip") == 0) {
fCurrentTooltip = val;
}
}
virtual void declare(const char* key, const char* val)
{}
//-------------------------------------------------------------------------------
// Simple API part
//-------------------------------------------------------------------------------
int getParamsCount() { return fNumParameters; }
int getParamIndex(const char* path)
{
if (fPathMap.find(path) != fPathMap.end()) {
return fPathMap[path];
} else if (fLabelMap.find(path) != fLabelMap.end()) {
return fLabelMap[path];
} else {
return -1;
}
}
const char* getParamAddress(int p) { return fName[p].c_str(); }
const char* getParamUnit(int p) { return fUnit[p].c_str(); }
const char* getParamTooltip(int p) { return fTooltip[p].c_str(); }
FAUSTFLOAT getParamMin(int p) { return fMin[p]; }
FAUSTFLOAT getParamMax(int p) { return fMax[p]; }
FAUSTFLOAT getParamStep(int p) { return fStep[p]; }
FAUSTFLOAT getParamInit(int p) { return fInit[p]; }
FAUSTFLOAT* getParamZone(int p) { return fZone[p]; }
FAUSTFLOAT getParamValue(int p) { return *fZone[p]; }
void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; }
double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); }
void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); }
double value2ratio(int p, double r) { return fConversion[p]->faust2ui(r); }
double ratio2value(int p, double r) { return fConversion[p]->ui2faust(r); }
/**
* Return the control type (kAcc, kGyr, or -1) for a given paramater
*
* @param p - the UI parameter index
*
* @return the type
*/
Type getParamType(int p)
{
if (p >= 0) {
if (getZoneIndex(fAcc, p, 0) != -1
|| getZoneIndex(fAcc, p, 1) != -1
|| getZoneIndex(fAcc, p, 2) != -1) {
return kAcc;
} else if (getZoneIndex(fGyr, p, 0) != -1
|| getZoneIndex(fGyr, p, 1) != -1
|| getZoneIndex(fGyr, p, 2) != -1) {
return kGyr;
}
}
return kNoType;
}
/**
* Set a new value coming from an accelerometer, propagate it to all relevant float* zones.
*
* @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer
* @param value - the new value
*
*/
void propagateAcc(int acc, double value)
{
for (int i = 0; i < fAcc[acc].size(); i++) {
fAcc[acc][i]->update(value);
}
}
/**
* Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter.
*
* @param p - the UI parameter index
* @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping")
* @param curve - between 0 and 3
* @param amin - mapping 'min' point
* @param amid - mapping 'middle' point
* @param amax - mapping 'max' point
*
*/
void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax)
{
setConverter(fAcc, p, acc, curve, amin, amid, amax);
}
/**
* Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter.
*
* @param p - the UI parameter index
* @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping")
* @param curve - between 0 and 3
* @param amin - mapping 'min' point
* @param amid - mapping 'middle' point
* @param amax - mapping 'max' point
*
*/
void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax)
{
setConverter(fGyr, p, gyr, curve, amin, amid, amax);
}
/**
* Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter.
*
* @param p - the UI parameter index
* @param acc - the acc value to be retrieved (-1 means "no mapping")
* @param curve - the curve value to be retrieved
* @param amin - the amin value to be retrieved
* @param amid - the amid value to be retrieved
* @param amax - the amax value to be retrieved
*
*/
void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax)
{
getConverter(fAcc, p, acc, curve, amin, amid, amax);
}
/**
* Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter.
*
* @param p - the UI parameter index
* @param gyr - the gyr value to be retrieved (-1 means "no mapping")
* @param curve - the curve value to be retrieved
* @param amin - the amin value to be retrieved
* @param amid - the amid value to be retrieved
* @param amax - the amax value to be retrieved
*
*/
void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax)
{
getConverter(fGyr, p, gyr, curve, amin, amid, amax);
}
/**
* Set a new value coming from an gyroscope, propagate it to all relevant float* zones.
*
* @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope
* @param value - the new value
*
*/
void propagateGyr(int gyr, double value)
{
for (int i = 0; i < fGyr[gyr].size(); i++) {
fGyr[gyr][i]->update(value);
}
}
// getScreenColor() : -1 means no screen color control (no screencolor metadata found)
// otherwise return 0x00RRGGBB a ready to use color
int getScreenColor()
{
if (fHasScreenControl) {
int r = (fRedReader) ? fRedReader->getValue() : 0;
int g = (fGreenReader) ? fGreenReader->getValue() : 0;
int b = (fBlueReader) ? fBlueReader->getValue() : 0;
return (r<<16) | (g<<8) | b;
} else {
return -1;
}
}
};
#endif
class oscItem : public uiItem {
protected:
OSCSender* fSender;
String fPath;
public:
oscItem(OSCSender* sender, GUI* ui, const String& path, FAUSTFLOAT* zone)
:uiItem(ui, zone), fSender(sender), fPath(path) {}
virtual ~oscItem()
{}
virtual void reflectZone()
{
FAUSTFLOAT v = *fZone;
fCache = v;
fSender->send(fPath, float(v));
}
};
class JuceOSCUI : private OSCReceiver, private OSCReceiver::Listener<OSCReceiver::RealtimeCallback>, public GUI {
private:
OSCSender fSender;
String fIP;
int fInputPort, fOutputPort;
APIUI fAPIUI;
Array<oscItem*> fOSCItems; // Pointers are kept and desallocated by the GUI class
public:
JuceOSCUI(const std::string& ip, int in_port, int out_port)
:fIP(ip), fInputPort(in_port), fOutputPort(out_port)
{}
virtual ~JuceOSCUI()
{}
void oscMessageReceived(const OSCMessage& message) override
{
String address = message.getAddressPattern().toString();
for (int i = 0; i < message.size(); ++i) {
if (message[i].isFloat32()) {
fAPIUI.setParamValue(fAPIUI.getParamIndex(address.toStdString().c_str()), FAUSTFLOAT(message[i].getFloat32()));
// "get" message with correct address
} else if (message[i].isString()
&& message[i].getString().equalsIgnoreCase("get")
&& String(fAPIUI.getParamAddress(0)).startsWith(address)) {
for (int p = 0; p < fAPIUI.getParamsCount(); ++p) {
fSender.send(fAPIUI.getParamAddress(p), float(fAPIUI.getParamValue(p)), float(fAPIUI.getParamMin(p)), float(fAPIUI.getParamMax(p)));
}
// "hello" message
} else if (message[i].isString()
&& address.equalsIgnoreCase("/*")
&& message[i].getString().equalsIgnoreCase("hello")) {
String path = fAPIUI.getParamAddress(0);
int pos1 = path.indexOfChar('/');
int pos2 = path.indexOfChar(pos1 + 1, '/');
fSender.send(path.substring(pos1, pos2), fIP, fInputPort, fOutputPort);
}
}
}
bool run() override
{
// Keep all zones for update when OSC messages are received
if (fOSCItems.size() == 0) {
for (int p = 0; p < fAPIUI.getParamsCount(); ++p) {
fOSCItems.add(new oscItem(&fSender, this, fAPIUI.getParamAddress(p), fAPIUI.getParamZone(p)));
}
}
if (!fSender.connect(fIP, fOutputPort)) {
std::cerr << "Error: could not connect to UDP port " << fInputPort << std::endl;
return false;
}
if (!connect(fInputPort)) {
std::cerr << "Error: could not connect to UDP port " << fOutputPort << std::endl;
return false;
}
addListener(this);
return true;
}
void stop() override
{
fSender.disconnect();
disconnect();
removeListener(this);
}
// -- widget's layouts
void openTabBox(const char* label) override { fAPIUI.openTabBox(label); }
void openHorizontalBox(const char* label) override { fAPIUI.openHorizontalBox(label); }
void openVerticalBox(const char* label) override { fAPIUI.openVerticalBox(label); }
void closeBox() override { fAPIUI.closeBox(); }
// -- active widgets
void addButton(const char* label, FAUSTFLOAT* zone) override { fAPIUI.addButton(label, zone); }
void addCheckButton(const char* label, FAUSTFLOAT* zone) override { fAPIUI.addCheckButton(label, zone); }
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override
{ fAPIUI.addVerticalSlider(label, zone, init, min, max, step); }
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override
{ fAPIUI.addHorizontalSlider(label, zone, init, min, max, step); }
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override
{ fAPIUI.addNumEntry(label, zone, init, min, max, step); }
// -- passive widgets
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override
{ fAPIUI.addHorizontalBargraph(label, zone, min, max); }
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override
{ fAPIUI.addVerticalBargraph(label, zone, min, max); }
// -- metadata declarations
void declare(FAUSTFLOAT* zone, const char* key, const char* val) override { fAPIUI.declare(zone, key, val); }
};
#endif // __juce_osc__
#endif //OSCCTRL
#if defined(MIDICTRL)
/************************************************************************
FAUST Architecture File
Copyright (C) 2016 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.
************************************************************************
************************************************************************/
#ifndef __juce_midi__
#define __juce_midi__
#include "juce_MidiMessage.h"
#include "juce_CriticalSection.h"
class MapUI;
class juce_midi_handler : public midi_handler {
protected:
MidiBuffer fOutputBuffer;
CriticalSection fMutex;
void decodeMessage(const MidiMessage& message)
{
const uint8* data = message.getRawData();
if (message.isNoteOff()) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->keyOff(0, message.getChannel(), data[1], data[2]);
}
} else if (message.isNoteOn()) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
if (data[1] != 0) {
fMidiInputs[i]->keyOn(0, message.getChannel(), data[1], data[2]);
} else {
fMidiInputs[i]->keyOff(0, message.getChannel(), data[1], data[2]);
}
}
} else if (message.isAftertouch()) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->keyPress(0, message.getChannel(), data[1], data[2]);
}
} else if (message.isController()) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->ctrlChange(0, message.getChannel(), data[1], data[2]);
}
} else if (message.isProgramChange()) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->progChange(0, message.getChannel(), data[1]);
}
} else if (message.isChannelPressure()) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->chanPress(0, message.getChannel(), data[1]);
}
} else if (message.isPitchWheel()) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->pitchWheel(0, message.getChannel(), ((data[1] * 128.0 + data[2]) - 8192) / 8192.0);
}
} else if (message.isMidiClock()) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->clock(message.getTimeStamp());
}
} else if (message.isMidiStart()) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->start_sync(message.getTimeStamp());
}
} else if (message.isMidiStop()) {
for (unsigned int i = 0; i < fMidiInputs.size(); i++) {
fMidiInputs[i]->stop_sync(message.getTimeStamp());
}
} else {
std::cerr << "Unused MIDI message" << std::endl;
}
}
public:
juce_midi_handler():midi_handler("JUCE")
{}
void encodeBuffer(MidiBuffer& buffer)
{
const ScopedTryLock lock(fMutex);
if (lock.isLocked()) {
buffer.swapWith(fOutputBuffer);
fOutputBuffer.clear();
} else {
std::cerr << "encodeBuffer fails..." << std::endl;
}
}
void decodeBuffer(MidiBuffer& buffer)
{
MidiMessage msg;
int ignore;
for (MidiBuffer::Iterator it(buffer); it.getNextEvent(msg, ignore);) {
decodeMessage(msg);
}
buffer.clear();
}
MapUI* keyOn(int channel, int pitch, int velocity)
{
fOutputBuffer.addEvent(MidiMessage::noteOn(channel + 1, pitch, uint8(velocity)), 0);
return 0;
}
void keyOff(int channel, int pitch, int velocity)
{
fOutputBuffer.addEvent(MidiMessage::noteOff(channel + 1, pitch, uint8(velocity)), 0);
}
void ctrlChange(int channel, int ctrl, int val)
{
fOutputBuffer.addEvent(MidiMessage::controllerEvent(channel + 1, ctrl, uint8(val)), 0);
}
void chanPress(int channel, int press)
{
fOutputBuffer.addEvent(MidiMessage::channelPressureChange(channel + 1, press), 0);
}
void progChange(int channel, int pgm)
{
fOutputBuffer.addEvent(MidiMessage::programChange(channel + 1, pgm), 0);
}
void keyPress(int channel, int pitch, int press)
{
fOutputBuffer.addEvent(MidiMessage::aftertouchChange(channel + 1, pitch, press), 0);
}
void pitchWheel(int channel, int wheel)
{
fOutputBuffer.addEvent(MidiMessage::pitchWheel(channel + 1, wheel), 0);
}
void ctrlChange14bits(int channel, int ctrl, int value) {}
void start_sync(double date)
{
fOutputBuffer.addEvent(MidiMessage::midiStart(), 0);
}
void stop_sync(double date)
{
fOutputBuffer.addEvent(MidiMessage::midiStop(), 0);
}
void clock(double date)
{
fOutputBuffer.addEvent(MidiMessage::midiClock(), 0);
}
};
class juce_midi : public juce_midi_handler, public MidiInputCallback {
private:
MidiInput* fMidiIn;
MidiOutput* fMidiOut;
void handleIncomingMidiMessage(MidiInput*, const MidiMessage& message)
{
decodeMessage(message);
}
public:
virtual ~juce_midi()
{
stop_midi();
}
bool start_midi()
{
if ((fMidiIn = MidiInput::openDevice(MidiInput::getDefaultDeviceIndex(), this)) == nullptr) {
return false;
}
if ((fMidiOut = MidiOutput::openDevice(MidiOutput::getDefaultDeviceIndex())) == nullptr) {
return false;
}
fMidiIn->start();
return true;
}
void stop_midi()
{
fMidiIn->stop();
delete fMidiIn;
delete fMidiOut;
}
MapUI* keyOn(int channel, int pitch, int velocity)
{
fMidiOut->sendMessageNow(MidiMessage::noteOn(channel + 1, pitch, uint8(velocity)));
return 0;
}
void keyOff(int channel, int pitch, int velocity)
{
fMidiOut->sendMessageNow(MidiMessage::noteOff(channel + 1, pitch, uint8(velocity)));
}
void ctrlChange(int channel, int ctrl, int val)
{
fMidiOut->sendMessageNow(MidiMessage::controllerEvent(channel + 1, ctrl, uint8(val)));
}
void chanPress(int channel, int press)
{
fMidiOut->sendMessageNow(MidiMessage::channelPressureChange(channel + 1, press));
}
void progChange(int channel, int pgm)
{
fMidiOut->sendMessageNow(MidiMessage::programChange(channel + 1, pgm));
}
void keyPress(int channel, int pitch, int press)
{
fMidiOut->sendMessageNow(MidiMessage::aftertouchChange(channel + 1, pitch, press));
}
void pitchWheel(int channel, int wheel)
{
fMidiOut->sendMessageNow(MidiMessage::pitchWheel(channel + 1, wheel));
}
void ctrlChange14bits(int channel, int ctrl, int value) {}
void start_sync(double date)
{
fMidiOut->sendMessageNow(MidiMessage::midiStart());
}
void stop_sync(double date)
{
fMidiOut->sendMessageNow(MidiMessage::midiStop());
}
void clock(double date)
{
fMidiOut->sendMessageNow(MidiMessage::midiClock());
}
};
#endif // __juce_midi__
#endif //MIDICTRL
/************************************************************************
FAUST Architecture File
Copyright (C) 2003-2017 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.
************************************************************************
************************************************************************/
#ifndef __poly_dsp__
#define __poly_dsp__
#include <stdio.h>
#include <string>
#include <math.h>
#include <float.h>
#include <algorithm>
#include <ostream>
#include <sstream>
#include <vector>
#include <limits.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) 2003-2011 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.
************************************************************************
************************************************************************/
#ifndef __proxy_dsp__
#define __proxy_dsp__
#include <vector>
#include <map>
#ifdef _WIN32
#include <windows.h>
#define snprintf _snprintf
#endif
inline FAUSTFLOAT STR2REAL(const std::string& s) { return (strtod(s.c_str(), NULL)); }
//-------------------------------------------------------------------
// Decode a dsp JSON description and implement 'buildUserInterface'
//-------------------------------------------------------------------
struct JSONUIDecoder {
std::string fName;
std::map<std::string, std::string> fMetadatas;
std::vector<itemInfo*> fUiItems;
FAUSTFLOAT* fInControl;
FAUSTFLOAT* fOutControl;
std::string fJSON;
int fNumInputs, fNumOutputs;
int fInputItems, fOutputItems;
JSONUIDecoder(const std::string& json)
{
fJSON = json;
const char* p = fJSON.c_str();
parseJson(p, fMetadatas, fUiItems);
// fMetadatas will contain the "meta" section as well as <name : val>, <inputs : val>, <ouputs : val> pairs
if (fMetadatas.find("name") != fMetadatas.end()) {
fName = fMetadatas["name"];
fMetadatas.erase("name");
} else {
fName = "";
}
if (fMetadatas.find("inputs") != fMetadatas.end()) {
fNumInputs = atoi(fMetadatas["inputs"].c_str());
fMetadatas.erase("inputs");
} else {
fNumInputs = -1;
}
if (fMetadatas.find("outputs") != fMetadatas.end()) {
fNumOutputs = atoi(fMetadatas["outputs"].c_str());
fMetadatas.erase("outputs");
} else {
fNumOutputs = -1;
}
vector<itemInfo*>::iterator it;
fInputItems = 0;
fOutputItems = 0;
for (it = fUiItems.begin(); it != fUiItems.end(); it++) {
string type = (*it)->type;
if (type == "vslider" || type == "hslider" || type == "nentry" || type == "button") {
fInputItems++;
} else if (type == "hbargraph" || type == "vbargraph") {
fOutputItems++;
}
}
fInControl = new FAUSTFLOAT[fInputItems];
fOutControl = new FAUSTFLOAT[fOutputItems];
}
virtual ~JSONUIDecoder()
{
vector<itemInfo*>::iterator it;
for (it = fUiItems.begin(); it != fUiItems.end(); it++) {
delete(*it);
}
delete [] fInControl;
delete [] fOutControl;
}
void metadata(Meta* m)
{
std::map<std::string, std::string>::iterator it;
for (it = fMetadatas.begin(); it != fMetadatas.end(); it++) {
m->declare((*it).first.c_str(), (*it).second.c_str());
}
}
void buildUserInterface(UI* ui)
{
// To be sure the floats are correctly encoded
char* tmp_local = setlocale(LC_ALL, NULL);
setlocale(LC_ALL, "C");
int counterIn = 0;
int counterOut = 0;
vector<itemInfo*>::iterator it;
for (it = fUiItems.begin(); it != fUiItems.end(); it++) {
bool isInItem = false;
bool isOutItem = false;
string type = (*it)->type;
FAUSTFLOAT init = STR2REAL((*it)->init);
FAUSTFLOAT min = STR2REAL((*it)->min);
FAUSTFLOAT max = STR2REAL((*it)->max);
FAUSTFLOAT step = STR2REAL((*it)->step);
if (type == "vslider" || type == "hslider" || type == "nentry" || type == "button") {
isInItem = true;
} else if (type == "hbargraph" || type == "vbargraph") {
isOutItem = true;
}
// Meta data declaration for input items
if ((*it)->type.find("group") == string::npos && (*it)->type.find("bargraph") == string::npos && (*it)->type != "close") {
fInControl[counterIn] = init;
for (int i = 0; i < (*it)->meta.size(); i++) {
ui->declare(&fInControl[counterIn], (*it)->meta[i].first.c_str(), (*it)->meta[i].second.c_str());
}
}
// Meta data declaration for output items
else if ((*it)->type.find("bargraph") != string::npos) {
fOutControl[counterOut] = init;
for (int i = 0; i < (*it)->meta.size(); i++) {
ui->declare(&fOutControl[counterOut], (*it)->meta[i].first.c_str(), (*it)->meta[i].second.c_str());
}
}
// Meta data declaration for group opening or closing
else {
for (int i = 0; i < (*it)->meta.size(); i++) {
ui->declare(0, (*it)->meta[i].first.c_str(), (*it)->meta[i].second.c_str());
}
}
if (type == "hgroup") {
ui->openHorizontalBox((*it)->label.c_str());
} else if (type == "vgroup") {
ui->openVerticalBox((*it)->label.c_str());
} else if (type == "tgroup") {
ui->openTabBox((*it)->label.c_str());
} else if (type == "vslider") {
ui->addVerticalSlider((*it)->label.c_str(), &fInControl[counterIn], init, min, max, step);
} else if (type == "hslider") {
ui->addHorizontalSlider((*it)->label.c_str(), &fInControl[counterIn], init, min, max, step);
} else if (type == "checkbox") {
ui->addCheckButton((*it)->label.c_str(), &fInControl[counterIn]);
} else if (type == "hbargraph") {
ui->addHorizontalBargraph((*it)->label.c_str(), &fOutControl[counterOut], min, max);
} else if (type == "vbargraph") {
ui->addVerticalBargraph((*it)->label.c_str(), &fOutControl[counterOut], min, max);
} else if (type == "nentry") {
ui->addNumEntry((*it)->label.c_str(), &fInControl[counterIn], init, min, max, step);
} else if (type == "button") {
ui->addButton((*it)->label.c_str(), &fInControl[counterIn]);
} else if (type == "close") {
ui->closeBox();
}
if (isInItem) {
counterIn++;
}
if (isOutItem) {
counterOut++;
}
}
setlocale(LC_ALL, tmp_local);
}
};
//----------------------------------------------------------------
// Proxy dsp definition created from the DSP JSON description
// This class allows a 'proxy' dsp to control a real dsp
// possibly running somewhere else.
//----------------------------------------------------------------
class proxy_dsp : public dsp {
private:
int fSamplingFreq;
JSONUIDecoder* fDecoder;
public:
proxy_dsp(const string& json)
{
fDecoder = new JSONUIDecoder(json);
fSamplingFreq = -1;
}
proxy_dsp(dsp* dsp)
{
JSONUI builder(dsp->getNumInputs(), dsp->getNumOutputs());
dsp->metadata(&builder);
dsp->buildUserInterface(&builder);
fSamplingFreq = dsp->getSampleRate();
fDecoder = new JSONUIDecoder(builder.JSON());
}
virtual ~proxy_dsp()
{
delete fDecoder;
}
virtual int getNumInputs() { return fDecoder->fNumInputs; }
virtual int getNumOutputs() { return fDecoder->fNumOutputs; }
virtual void buildUserInterface(UI* ui) { fDecoder->buildUserInterface(ui); }
// To possibly implement in a concrete proxy dsp
virtual void init(int samplingRate) { fSamplingFreq = samplingRate; }
virtual void instanceInit(int samplingRate) {}
virtual void instanceConstants(int samplingRate) {}
virtual void instanceResetUserInterface() {}
virtual void instanceClear() {}
virtual int getSampleRate() { return fSamplingFreq; }
virtual proxy_dsp* clone() { return new proxy_dsp(fDecoder->fJSON); }
virtual void metadata(Meta* m) { fDecoder->metadata(m); }
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {}
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {}
};
#endif
#define kActiveVoice 0
#define kFreeVoice -1
#define kReleaseVoice -2
#define kNoVoice -3
#define VOICE_STOP_LEVEL 0.001
#define MIX_BUFFER_SIZE 16384
#define FLOAT_MAX(a, b) (((a) < (b)) ? (b) : (a))
// endsWith(<str>,<end>) : returns true if <str> ends with <end>
static inline bool endsWith(std::string const& str, std::string const& end)
{
size_t l1 = str.length();
size_t l2 = end.length();
return (l1 >= l2) && (0 == str.compare(l1 - l2, l2, end));
}
static inline double midiToFreq(double note)
{
return 440.0 * pow(2.0, (note-69.0)/12.0);
}
static inline unsigned int isPowerOfTwo(unsigned int n)
{
return !(n & (n - 1));
}
/**
* Allows to control zones in a grouped manner.
*/
class GroupUI : public GUI, public PathBuilder
{
private:
std::map<std::string, uiGroupItem*> fLabelZoneMap;
void insertMap(std::string label, FAUSTFLOAT* zone)
{
if (!endsWith(label, "/gate")
&& !endsWith(label, "/freq")
&& !endsWith(label, "/gain")) {
// Groups all controller except 'freq', 'gate', and 'gain'
if (fLabelZoneMap.find(label) != fLabelZoneMap.end()) {
fLabelZoneMap[label]->addZone(zone);
} else {
fLabelZoneMap[label] = new uiGroupItem(this, zone);
}
}
}
uiCallbackItem* fPanic;
public:
GroupUI(FAUSTFLOAT* zone, uiCallback cb, void* arg)
{
fPanic = new uiCallbackItem(this, zone, cb, arg);
};
virtual ~GroupUI()
{
// 'fPanic' is kept and deleted in GUI, so do not delete here
};
// -- widget's layouts
void openTabBox(const char* label)
{
fControlsLevel.push_back(label);
}
void openHorizontalBox(const char* label)
{
fControlsLevel.push_back(label);
}
void openVerticalBox(const char* label)
{
fControlsLevel.push_back(label);
}
void closeBox()
{
fControlsLevel.pop_back();
}
// -- active widgets
void addButton(const char* label, FAUSTFLOAT* zone)
{
insertMap(buildPath(label), zone);
}
void addCheckButton(const char* label, FAUSTFLOAT* zone)
{
insertMap(buildPath(label), zone);
}
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step)
{
insertMap(buildPath(label), zone);
}
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step)
{
insertMap(buildPath(label), zone);
}
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step)
{
insertMap(buildPath(label), zone);
}
// -- passive widgets
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax)
{
insertMap(buildPath(label), zone);
}
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax)
{
insertMap(buildPath(label), zone);
}
};
/**
* One voice of polyphony.
*/
struct dsp_voice : public MapUI, public decorator_dsp {
int fNote; // Playing note actual pitch
int fDate; // KeyOn date
bool fTrigger; // True if stolen note and need for envelop trigger
FAUSTFLOAT fLevel; // Last audio block level
std::string fGatePath; // Path of 'gate' control
std::string fGainPath; // Path of 'gain' control
std::string fFreqPath; // Path of 'freq' control
dsp_voice(dsp* dsp):decorator_dsp(dsp)
{
dsp->buildUserInterface(this);
fNote = kFreeVoice;
fLevel = FAUSTFLOAT(0);
fDate = 0;
fTrigger = false;
extractPaths(fGatePath, fFreqPath, fGainPath);
}
void extractPaths(std::string& gate, std::string& freq, std::string& gain)
{
// Keep gain, freq and gate labels
std::map<std::string, FAUSTFLOAT*>::iterator it;
for (it = getMap().begin(); it != getMap().end(); it++) {
std::string path = (*it).first;
if (endsWith(path, "/gate")) {
gate = path;
} else if (endsWith(path, "/freq")) {
freq = path;
} else if (endsWith(path, "/gain")) {
gain = path;
}
}
}
// MIDI velocity [0..127]
void keyOn(int pitch, int velocity)
{
setParamValue(fFreqPath, midiToFreq(pitch));
setParamValue(fGainPath, float(velocity)/127.f);
fNote = pitch;
fTrigger = true; // so that envelop is always re-initialized
}
// Normalized MIDI velocity [0..1]
void keyOn(int pitch, float velocity)
{
setParamValue(fFreqPath, midiToFreq(pitch));
setParamValue(fGainPath, velocity);
fNote = pitch;
fTrigger = true; // so that envelop is always re-initialized
}
void keyOff(bool hard = false)
{
// No use of velocity for now...
setParamValue(fGatePath, FAUSTFLOAT(0));
if (hard) {
// Stop immediately
fNote = kFreeVoice;
fTrigger = false;
} else {
// Release voice
fNote = kReleaseVoice;
}
}
void play(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
if (fTrigger) {
// New note, so trigger it
trigger(count, inputs, outputs);
} else {
// Compute the voice
compute(count, inputs, outputs);
}
}
void trigger(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
setParamValue(fGatePath, FAUSTFLOAT(0));
computeSlice(0, 1, inputs, outputs);
setParamValue(fGatePath, FAUSTFLOAT(1));
computeSlice(1, count - 1, inputs, outputs);
fTrigger = false;
}
void computeSlice(int offset, int slice, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
if (slice > 0) {
FAUSTFLOAT** inputs_slice = (FAUSTFLOAT**)alloca(getNumInputs() * sizeof(FAUSTFLOAT*));
for (int chan = 0; chan < getNumInputs(); chan++) {
inputs_slice[chan] = &(inputs[chan][offset]);
}
FAUSTFLOAT** outputs_slice = (FAUSTFLOAT**)alloca(getNumOutputs() * sizeof(FAUSTFLOAT*));
for (int chan = 0; chan < getNumOutputs(); chan++) {
outputs_slice[chan] = &(outputs[chan][offset]);
}
compute(slice, inputs_slice, outputs_slice);
}
}
};
/**
* A group of voices.
*/
struct dsp_voice_group {
GroupUI fGroups;
std::vector<dsp_voice*> fVoiceTable; // Individual voices
dsp* fVoiceGroup; // Voices group to be used for GUI grouped control
FAUSTFLOAT fPanic;
bool fVoiceControl;
bool fGroupControl;
dsp_voice_group(uiCallback cb, void* arg, bool control, bool group)
:fGroups(&fPanic, cb, arg),
fVoiceGroup(0), fPanic(FAUSTFLOAT(0)),
fVoiceControl(control), fGroupControl(group)
{}
virtual ~dsp_voice_group()
{
for (int i = 0; i < fVoiceTable.size(); i++) {
delete fVoiceTable[i];
}
delete fVoiceGroup;
}
void addVoice(dsp_voice* voice)
{
fVoiceTable.push_back(voice);
}
void clearVoices()
{
fVoiceTable.clear();
}
void init()
{
// Groups all uiItem for a given path
fVoiceGroup = new proxy_dsp(fVoiceTable[0]);
fVoiceGroup->buildUserInterface(&fGroups);
for (int i = 0; i < fVoiceTable.size(); i++) {
fVoiceTable[i]->buildUserInterface(&fGroups);
}
}
void buildUserInterface(UI* ui_interface)
{
if (fVoiceTable.size() > 1) {
ui_interface->openTabBox("Polyphonic");
// Grouped voices UI
ui_interface->openVerticalBox("Voices");
ui_interface->addButton("Panic", &fPanic);
fVoiceGroup->buildUserInterface(ui_interface);
ui_interface->closeBox();
// In not group, also add individual voices UI
if (!fGroupControl) {
for (int i = 0; i < fVoiceTable.size(); i++) {
char buffer[32];
snprintf(buffer, 31, ((fVoiceTable.size() < 8) ? "Voice%d" : "V%d"), i+1);
ui_interface->openHorizontalBox(buffer);
fVoiceTable[i]->buildUserInterface(ui_interface);
ui_interface->closeBox();
}
}
ui_interface->closeBox();
} else {
fVoiceTable[0]->buildUserInterface(ui_interface);
}
}
};
/**
* Polyphonic DSP : group a set of DSP to be played together or triggered by MIDI.
*/
class mydsp_poly : public decorator_dsp, public dsp_voice_group, public midi {
private:
std::vector<MidiUI*> fMidiUIList;
FAUSTFLOAT** fMixBuffer;
int fDate;
inline FAUSTFLOAT mixVoice(int count, FAUSTFLOAT** outputBuffer, FAUSTFLOAT** mixBuffer)
{
FAUSTFLOAT level = 0;
for (int i = 0; i < getNumOutputs(); i++) {
FAUSTFLOAT* mixChannel = mixBuffer[i];
FAUSTFLOAT* outChannel = outputBuffer[i];
for (int j = 0; j < count; j++) {
level = FLOAT_MAX(level, (FAUSTFLOAT)fabs(outChannel[j]));
mixChannel[j] += outChannel[j];
}
}
return level;
}
inline void clearOutput(int count, FAUSTFLOAT** mixBuffer)
{
for (int i = 0; i < getNumOutputs(); i++) {
memset(mixBuffer[i], 0, count * sizeof(FAUSTFLOAT));
}
}
inline int getVoice(int note, bool steal = false)
{
for (int i = 0; i < fVoiceTable.size(); i++) {
if (fVoiceTable[i]->fNote == note) {
if (steal) {
fVoiceTable[i]->fDate = fDate++;
}
return i;
}
}
if (steal) {
int voice_release = kNoVoice;
int voice_playing = kNoVoice;
int oldest_date_release = INT_MAX;
int oldest_date_playing = INT_MAX;
// Scan all voices
for (int i = 0; i < fVoiceTable.size(); i++) {
if (fVoiceTable[i]->fNote == kReleaseVoice) {
// Keeps oldest release voice
if (fVoiceTable[i]->fDate < oldest_date_release) {
oldest_date_release = fVoiceTable[i]->fDate;
voice_release = i;
}
} else {
// Otherwise keeps oldest playing voice
if (fVoiceTable[i]->fDate < oldest_date_playing) {
oldest_date_playing = fVoiceTable[i]->fDate;
voice_playing = i;
}
}
}
// Then decide which one to steal
if (oldest_date_release != INT_MAX) {
std::cout << "Steal release voice : voice_date " << fVoiceTable[voice_release]->fDate << " cur_date = " << fDate << " voice = " << voice_release << std::endl;
fVoiceTable[voice_release]->fDate = fDate++;
fVoiceTable[voice_release]->fTrigger = true;
return voice_release;
} else if (oldest_date_playing != INT_MAX) {
std::cout << "Steal playing voice : voice_date " << fVoiceTable[voice_playing]->fDate << " cur_date = " << fDate << " voice = " << voice_playing << std::endl;
fVoiceTable[voice_playing]->fDate = fDate++;
fVoiceTable[voice_playing]->fTrigger = true;
return voice_playing;
} else {
assert(false);
return kNoVoice;
}
} else {
return kNoVoice;
}
}
static void panic(FAUSTFLOAT val, void* arg)
{
if (val == FAUSTFLOAT(1)) {
static_cast<mydsp_poly*>(arg)->allNotesOff(true);
}
}
inline bool checkPolyphony()
{
if (fVoiceTable.size() > 1) {
return true;
} else {
std::cout << "DSP is not polyphonic...\n";
return false;
}
}
// Always returns a voice
int newVoiceAux()
{
int voice = getVoice(kFreeVoice, true);
assert(voice != kNoVoice);
fVoiceTable[voice]->fNote = kActiveVoice;
return voice;
}
public:
/**
* Constructor.
*
* @param dsp - the dsp to be used for one voice. Beware: mydsp_poly will use and finally delete the pointer.
* @param nvoices - number of polyphony voices
* @param control - whether voices will be dynamically allocated and controlled (typically by a MIDI controler).
* If false all voices are always running.
* @param group - if true, voices are not individually accessible, a global "Voices" tab will automatically dispatch
* a given control on all voices, assuming GUI::updateAllGuis() is called.
* If false, all voices can be individually controlled.
*
*/
mydsp_poly(dsp* dsp,
int nvoices,
bool control = false,
bool group = true):decorator_dsp(dsp), dsp_voice_group(panic, this, control, group)
{
fDate = 0;
// Create voices
for (int i = 0; i < nvoices; i++) {
addVoice(new dsp_voice(dsp->clone()));
}
// Init audio output buffers
fMixBuffer = new FAUSTFLOAT*[getNumOutputs()];
for (int i = 0; i < getNumOutputs(); i++) {
fMixBuffer[i] = new FAUSTFLOAT[MIX_BUFFER_SIZE];
}
dsp_voice_group::init();
}
virtual ~mydsp_poly()
{
for (int i = 0; i < getNumOutputs(); i++) {
delete[] fMixBuffer[i];
}
delete[] fMixBuffer;
// Remove object from all MidiUI interfaces that handle it
for (int i = 0; i < fMidiUIList.size(); i++) {
fMidiUIList[i]->removeMidiIn(this);
}
}
// DSP API
void buildUserInterface(UI* ui_interface)
{
// Add itself to the MidiUI object
MidiUI* midi_ui = dynamic_cast<MidiUI*>(ui_interface);
if (midi_ui) {
fMidiUIList.push_back(midi_ui);
midi_ui->addMidiIn(this);
}
dsp_voice_group::buildUserInterface(ui_interface);
}
void init(int samplingRate)
{
// Init voices
for (int i = 0; i < fVoiceTable.size(); i++) {
fVoiceTable[i]->init(samplingRate);
}
}
void instanceInit(int samplingRate)
{
// Init voices
for (int i = 0; i < fVoiceTable.size(); i++) {
fVoiceTable[i]->instanceInit(samplingRate);
}
}
void instanceConstants(int samplingRate)
{
// Init voices
for (int i = 0; i < fVoiceTable.size(); i++) {
fVoiceTable[i]->instanceConstants(samplingRate);
}
}
void instanceResetUserInterface()
{
for (int i = 0; i < fVoiceTable.size(); i++) {
fVoiceTable[i]->instanceResetUserInterface();
}
}
void instanceClear()
{
for (int i = 0; i < fVoiceTable.size(); i++) {
fVoiceTable[i]->instanceClear();
}
}
virtual mydsp_poly* clone()
{
return new mydsp_poly(fDSP->clone(), fVoiceTable.size(), fVoiceControl, fGroupControl);
}
void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
assert(count < MIX_BUFFER_SIZE);
// First clear the outputs
clearOutput(count, outputs);
if (fVoiceControl) {
// Mix all playing voices
for (int i = 0; i < fVoiceTable.size(); i++) {
dsp_voice* voice = fVoiceTable[i];
if (voice->fNote != kFreeVoice) {
voice->play(count, inputs, fMixBuffer);
// Mix it in result
voice->fLevel = mixVoice(count, fMixBuffer, outputs);
// Check the level to possibly set the voice in kFreeVoice again
if ((voice->fLevel < VOICE_STOP_LEVEL) && (voice->fNote == kReleaseVoice)) {
voice->fNote = kFreeVoice;
}
}
}
} else {
// Mix all voices
for (int i = 0; i < fVoiceTable.size(); i++) {
fVoiceTable[i]->compute(count, inputs, fMixBuffer);
mixVoice(count, fMixBuffer, outputs);
}
}
}
void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
compute(count, inputs, outputs);
}
// Additional polyphonic API
MapUI* newVoice()
{
return fVoiceTable[newVoiceAux()];
}
void deleteVoice(MapUI* voice)
{
std::vector<dsp_voice*>::iterator it = find(fVoiceTable.begin(), fVoiceTable.end(), reinterpret_cast<dsp_voice*>(voice));
if (it != fVoiceTable.end()) {
(*it)->keyOff();
} else {
std::cout << "Voice not found\n";
}
}
// MIDI API
MapUI* keyOn(int channel, int pitch, int velocity)
{
if (checkPolyphony()) {
int voice = newVoiceAux();
fVoiceTable[voice]->keyOn(pitch, velocity);
return fVoiceTable[voice];
} else {
return 0;
}
}
void keyOff(int channel, int pitch, int velocity = 127)
{
if (checkPolyphony()) {
int voice = getVoice(pitch);
if (voice != kNoVoice) {
fVoiceTable[voice]->keyOff();
} else {
std::cout << "Playing pitch = " << pitch << " not found\n";
}
}
}
void pitchWheel(int channel, int wheel)
{}
void ctrlChange(int channel, int ctrl, int value)
{
if (ctrl == ALL_NOTES_OFF || ctrl == ALL_SOUND_OFF) {
allNotesOff();
}
}
void progChange(int channel, int pgm)
{}
void keyPress(int channel, int pitch, int press)
{}
void chanPress(int channel, int press)
{}
void ctrlChange14bits(int channel, int ctrl, int value)
{}
// Terminate all active voices, gently or immediately (depending of 'hard' value)
void allNotesOff(bool hard = false)
{
for (int i = 0; i < fVoiceTable.size(); i++) {
fVoiceTable[i]->keyOff(hard);
}
}
};
#endif // __poly_dsp__
#if defined(POLY2)
/************************************************************************
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) 2003-2011 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.
************************************************************************
************************************************************************/
#ifndef __dsp_combiner__
#define __dsp_combiner__
#include <string.h>
#include <assert.h>
// Combine two DSP in sequence
class dsp_sequencer : public dsp {
private:
dsp* fDSP1;
dsp* fDSP2;
FAUSTFLOAT** fSeqBuffer;
public:
dsp_sequencer(dsp* dsp1, dsp* dsp2, int buffer_size = 4096)
:fDSP1(dsp1), fDSP2(dsp2)
{
assert(fDSP1->getNumOutputs() == fDSP2->getNumInputs());
fSeqBuffer = new FAUSTFLOAT*[fDSP1->getNumOutputs()];
for (int i = 0; i < fDSP1->getNumOutputs(); i++) {
fSeqBuffer[i] = new FAUSTFLOAT[buffer_size];
}
}
virtual ~dsp_sequencer()
{
for (int i = 0; i < fDSP1->getNumOutputs(); i++) {
delete [] fSeqBuffer[i];
}
delete [] fSeqBuffer;
delete fDSP1;
delete fDSP2;
}
virtual int getNumInputs() { return fDSP1->getNumInputs(); }
virtual int getNumOutputs() { return fDSP2->getNumOutputs(); }
virtual void buildUserInterface(UI* ui_interface)
{
ui_interface->openTabBox("Sequencer");
ui_interface->openVerticalBox("DSP1");
fDSP1->buildUserInterface(ui_interface);
ui_interface->closeBox();
ui_interface->openVerticalBox("DSP2");
fDSP2->buildUserInterface(ui_interface);
ui_interface->closeBox();
ui_interface->closeBox();
}
virtual int getSampleRate()
{
return fDSP1->getSampleRate();
}
virtual void init(int samplingRate)
{
fDSP1->init(samplingRate);
fDSP2->init(samplingRate);
}
virtual void instanceInit(int samplingRate)
{
fDSP1->instanceInit(samplingRate);
fDSP2->instanceInit(samplingRate);
}
virtual void instanceConstants(int samplingRate)
{
fDSP1->instanceConstants(samplingRate);
fDSP2->instanceConstants(samplingRate);
}
virtual void instanceResetUserInterface()
{
fDSP1->instanceResetUserInterface();
fDSP2->instanceResetUserInterface();
}
virtual void instanceClear()
{
fDSP1->instanceClear();
fDSP2->instanceClear();
}
virtual dsp* clone()
{
return new dsp_sequencer(fDSP1->clone(), fDSP2->clone());
}
virtual void metadata(Meta* m)
{
fDSP1->metadata(m);
fDSP2->metadata(m);
}
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
fDSP1->compute(count, inputs, fSeqBuffer);
fDSP2->compute(count, fSeqBuffer, outputs);
}
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
};
// Combine two DSP in parallel
class dsp_parallelizer : public dsp {
private:
dsp* fDSP1;
dsp* fDSP2;
public:
dsp_parallelizer(dsp* dsp1, dsp* dsp2, int buffer_size = 4096)
:fDSP1(dsp1), fDSP2(dsp2)
{}
virtual ~dsp_parallelizer()
{
delete fDSP1;
delete fDSP2;
}
virtual int getNumInputs() { return fDSP1->getNumInputs() + fDSP2->getNumInputs(); }
virtual int getNumOutputs() { return fDSP1->getNumOutputs() + fDSP2->getNumOutputs(); }
virtual void buildUserInterface(UI* ui_interface)
{
ui_interface->openTabBox("Parallelizer");
ui_interface->openVerticalBox("DSP1");
fDSP1->buildUserInterface(ui_interface);
ui_interface->closeBox();
ui_interface->openVerticalBox("DSP2");
fDSP2->buildUserInterface(ui_interface);
ui_interface->closeBox();
ui_interface->closeBox();
}
virtual int getSampleRate()
{
return fDSP1->getSampleRate();
}
virtual void init(int samplingRate)
{
fDSP1->init(samplingRate);
fDSP2->init(samplingRate);
}
virtual void instanceInit(int samplingRate)
{
fDSP1->instanceInit(samplingRate);
fDSP2->instanceInit(samplingRate);
}
virtual void instanceConstants(int samplingRate)
{
fDSP1->instanceConstants(samplingRate);
fDSP2->instanceConstants(samplingRate);
}
virtual void instanceResetUserInterface()
{
fDSP1->instanceResetUserInterface();
fDSP2->instanceResetUserInterface();
}
virtual void instanceClear()
{
fDSP1->instanceClear();
fDSP2->instanceClear();
}
virtual dsp* clone()
{
return new dsp_parallelizer(fDSP1->clone(), fDSP2->clone());
}
virtual void metadata(Meta* m)
{
fDSP1->metadata(m);
fDSP2->metadata(m);
}
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
{
fDSP1->compute(count, inputs, outputs);
// Shift inputs/outputs channels for fDSP2
FAUSTFLOAT** inputs_dsp2 = (FAUSTFLOAT**)alloca(fDSP2->getNumInputs() * sizeof(FAUSTFLOAT*));
for (int chan = 0; chan < fDSP2->getNumInputs(); chan++) {
inputs_dsp2[chan] = inputs[fDSP1->getNumInputs() + chan];
}
FAUSTFLOAT** outputs_dsp2 = (FAUSTFLOAT**)alloca(fDSP2->getNumOutputs() * sizeof(FAUSTFLOAT*));
for (int chan = 0; chan < fDSP2->getNumOutputs(); chan++) {
outputs_dsp2[chan] = inputs[fDSP1->getNumOutputs() + chan];
}
fDSP2->compute(count, inputs_dsp2, outputs_dsp2);
}
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
};
#endif
#include "dsp_effect.cpp"
#endif //POLY POLY2
#ifndef FAUSTFLOAT
#define FAUSTFLOAT float
#endif
#ifndef FAUSTCLASS
#define FAUSTCLASS mydsp
#endif
class mydsp : public dsp {
private:
FAUSTFLOAT fslider0;
float fRec0[2];
int fSamplingFreq;
public:
virtual void metadata(Meta* m) {
m->declare("name", "GainCtl");
m->declare("version", "0.1");
m->declare("author", "Xavier Godart");
m->declare("copyright", "Xavier Godart");
m->declare("filter.lib/name", "Faust Filter Library");
m->declare("filter.lib/author", "Julius O. Smith (jos at ccrma.stanford.edu)");
m->declare("filter.lib/copyright", "Julius O. Smith III");
m->declare("filter.lib/version", "1.29");
m->declare("filter.lib/license", "STK-4.3");
m->declare("filter.lib/reference", "https://ccrma.stanford.edu/~jos/filters/");
m->declare("filter.lib/deprecated", "This library is deprecated and is not maintained anymore. It will be removed in August 2017.");
m->declare("music.lib/name", "Music Library");
m->declare("music.lib/author", "GRAME");
m->declare("music.lib/copyright", "GRAME");
m->declare("music.lib/version", "1.0");
m->declare("music.lib/license", "LGPL with exception");
m->declare("music.lib/deprecated", "This library is deprecated and is not maintained anymore. It will be removed in August 2017.");
m->declare("math.lib/name", "Math Library");
m->declare("math.lib/author", "GRAME");
m->declare("math.lib/copyright", "GRAME");
m->declare("math.lib/version", "1.0");
m->declare("math.lib/license", "LGPL with exception");
m->declare("math.lib/deprecated", "This library is deprecated and is not maintained anymore. It will be removed in August 2017.");
}
virtual int getNumInputs() { return 1; }
virtual int getNumOutputs() { return 1; }
static void classInit(int samplingFreq) {
}
virtual void instanceConstants(int samplingFreq) {
fSamplingFreq = samplingFreq;
}
virtual void instanceResetUserInterface() {
fslider0 = 0.5f;
}
virtual void instanceClear() {
for (int i=0; i<2; i++) fRec0[i] = 0;
}
virtual void init(int samplingFreq) {
classInit(samplingFreq);
instanceInit(samplingFreq);
}
virtual void instanceInit(int samplingFreq) {
instanceConstants(samplingFreq);
instanceResetUserInterface();
instanceClear();
}
virtual mydsp* clone() {
return new mydsp();
}
virtual int getSampleRate() {
return fSamplingFreq;
}
virtual void buildUserInterface(UI* ui_interface) {
ui_interface->openVerticalBox("0x00");
ui_interface->addHorizontalSlider("Gain", &fslider0, 0.5f, 0.0f, 1.0f, 0.01f);
ui_interface->closeBox();
}
virtual void compute (int count, FAUSTFLOAT** input, FAUSTFLOAT** output) {
float fSlow0 = (0.001f * float(fslider0));
FAUSTFLOAT* input0 = input[0];
FAUSTFLOAT* output0 = output[0];
for (int i=0; i<count; i++) {
fRec0[0] = (fSlow0 + (0.999f * fRec0[1]));
output0[i] = (FAUSTFLOAT)((float)input0[i] * fRec0[0]);
// post processing
fRec0[1] = fRec0[0];
}
}
};
#if defined(JUCE_POLY)
struct FaustSound : public SynthesiserSound {
bool appliesToNote (int /*midiNoteNumber*/) override { return true; }
bool appliesToChannel (int /*midiChannel*/) override { return true; }
};
// An hybrid JUCE and Faust voice
class FaustVoice : public SynthesiserVoice, public dsp_voice {
private:
ScopedPointer<AudioBuffer<FAUSTFLOAT>> fBuffer;
public:
FaustVoice(dsp* dsp):dsp_voice(dsp)
{
// Allocate buffer for mixing
fBuffer = new AudioBuffer<FAUSTFLOAT>(dsp->getNumOutputs(), 8192);
fDSP->init(SynthesiserVoice::getSampleRate());
}
bool canPlaySound (SynthesiserSound* sound) override
{
return dynamic_cast<FaustSound*> (sound) != nullptr;
}
void startNote (int midiNoteNumber,
float velocity,
SynthesiserSound* s,
int currentPitchWheelPosition) override
{
keyOn(midiNoteNumber, velocity);
}
void stopNote (float velocity, bool allowTailOff) override
{
keyOff(!allowTailOff);
clearCurrentNote();
}
void pitchWheelMoved (int newPitchWheelValue) override
{
// not implemented for now
}
void controllerMoved (int controllerNumber, int newControllerValue) override
{
// not implemented for now
}
void renderNextBlock (AudioBuffer<FAUSTFLOAT>& outputBuffer,
int startSample,
int numSamples) override
{
// Play the voice
play(numSamples, nullptr, (FAUSTFLOAT**)fBuffer->getArrayOfReadPointers());
// Mix it in outputs
for (int i = 0; i < fDSP->getNumOutputs(); i++) {
outputBuffer.addFrom(i, startSample, *fBuffer, i, 0, numSamples);
}
}
};
// Decorates the JUCE Synthesiser and adds Faust polyphonic code for GUI handling
class FaustSynthesiser : public Synthesiser, public dsp_voice_group {
private:
ScopedPointer<Synthesiser> fSynth;
public:
FaustSynthesiser(uiCallback cb, void* arg):dsp_voice_group(cb, arg, true, true), fSynth(new Synthesiser())
{}
virtual ~FaustSynthesiser()
{
// Voices will be deallocated by fSynth
dsp_voice_group::clearVoices();
}
void addVoice(FaustVoice* voice)
{
fSynth->addVoice(voice);
dsp_voice_group::addVoice(voice);
}
void addSound(SynthesiserSound* sound)
{
fSynth->addSound(sound);
}
void allNotesOff(int midiChannel, bool allowTailOff)
{
fSynth->allNotesOff(midiChannel, allowTailOff);
}
void setCurrentPlaybackSampleRate (double newRate)
{
fSynth->setCurrentPlaybackSampleRate(newRate);
}
void renderNextBlock (AudioBuffer<float>& outputAudio,
const MidiBuffer& inputMidi,
int startSample,
int numSamples)
{
fSynth->renderNextBlock(outputAudio, inputMidi, startSample, numSamples);
}
void renderNextBlock (AudioBuffer<double>& outputAudio,
const MidiBuffer& inputMidi,
int startSample,
int numSamples)
{
fSynth->renderNextBlock(outputAudio, inputMidi, startSample, numSamples);
}
};
#endif
class FaustPlugInAudioProcessor : public AudioProcessor, private Timer
{
public:
FaustPlugInAudioProcessor();
~FaustPlugInAudioProcessor();
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{
jassert (! isUsingDoublePrecision());
process (buffer, midiMessages);
}
void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
{
jassert (isUsingDoublePrecision());
process (buffer, midiMessages);
}
AudioProcessorEditor* createEditor() override;
bool hasEditor() const override;
const String getName() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
double getTailLengthSeconds() const override;
int getNumPrograms() override;
int getCurrentProgram() override;
void setCurrentProgram (int index) override;
const String getProgramName (int index) override;
void changeProgramName (int index, const String& newName) override;
void getStateInformation (MemoryBlock& destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
void releaseResources() override
{}
void timerCallback() override;
AudioProcessor::BusesProperties getBusesProperties();
bool supportsDoublePrecisionProcessing() const override;
static void panic(float val, void* arg);
#ifdef JUCE_POLY
ScopedPointer<FaustSynthesiser> fSynth;
#else
#if defined(MIDICTRL)
ScopedPointer<juce_midi_handler> fMIDIHandler;
ScopedPointer<MidiUI> fMIDIUI;
#endif
ScopedPointer<dsp> fDSP;
#endif
#if defined(OSCCTRL)
ScopedPointer<JuceOSCUI> fOSCUI;
#endif
JuceStateUI fStateUI;
JuceParameterUI fParameterUI;
private:
template <typename FloatType>
void process (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FaustPlugInAudioProcessor)
};
class FaustPlugInAudioProcessorEditor : public AudioProcessorEditor
{
public:
FaustPlugInAudioProcessorEditor (FaustPlugInAudioProcessor&);
~FaustPlugInAudioProcessorEditor();
void paint (Graphics&) override;
void resized() override;
private:
// This reference is provided as a quick way for your editor to
// access the processor object that created it.
FaustPlugInAudioProcessor& processor;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FaustPlugInAudioProcessorEditor)
JuceGUI juceGUI;
};
static mydsp gDSP;
FaustPlugInAudioProcessor::FaustPlugInAudioProcessor()
: AudioProcessor (getBusesProperties()), fParameterUI(this)
{
bool midi_sync = false;
int nvoices = 1;
mydsp* tmp_dsp = new mydsp();
MidiMeta::analyse(tmp_dsp, midi_sync, nvoices);
delete tmp_dsp;;
#ifdef JUCE_POLY
fSynth = new FaustSynthesiser(panic, this);
for (int i = 0; i < nvoices; i++) {
fSynth->addVoice(new FaustVoice(new mydsp()));
}
fSynth->init();
fSynth->addSound(new FaustSound());
#else
bool group = true;
#ifdef POLY2
std::cout << "Started with " << nvoices << " voices\n";
#if MIDICTRL
if (midi_sync) {
fDSP = new timed_dsp(new dsp_sequencer(new mydsp_poly(new mydsp(), nvoices, true, group), new dsp_effect()));
} else {
fDSP = new dsp_sequencer(new mydsp_poly(new mydsp(), nvoices, true, group), new dsp_effect());
}
#else
fDSP = new dsp_sequencer(new mydsp_poly(new mydsp(), nvoices, false, group), new dsp_effect());
#endif
#else
if (nvoices > 1) {
std::cout << "Started with " << nvoices << " voices\n";
#if MIDICTRL
if (midi_sync) {
fDSP = new timed_dsp(new mydsp_poly(new mydsp(), nvoices, true, group));
} else {
fDSP = new mydsp_poly(new mydsp(), nvoices, true, group);
}
#else
fDSP = new mydsp_poly(new mydsp(), nvoices, false, group);
#endif
} else {
#if MIDICTRL
if (midi_sync) {
fDSP = new timed_dsp(new mydsp());
} else {
fDSP = new mydsp();
}
#else
fDSP = new mydsp();
#endif
}
#endif
#if defined(MIDICTRL)
fMIDIHandler = new juce_midi_handler();
fMIDIUI = new MidiUI(fMIDIHandler);
fDSP->buildUserInterface(fMIDIUI);
if (!fMIDIUI->run()) {
std::cerr << "JUCE MIDI handler cannot be started..." << std::endl;
}
#endif
#endif
#if defined(OSCCTRL)
fOSCUI = new JuceOSCUI("127.0.0.1", 5510, 5511);
#ifdef JUCE_POLY
fSynth->buildUserInterface(fOSCUI);
#else
fDSP->buildUserInterface(fOSCUI);
#endif
if (!fOSCUI->run()) {
std::cerr << "JUCE OSC handler cannot be started..." << std::endl;
}
#endif
#ifdef JUCE_POLY
fSynth->buildUserInterface(&fStateUI);
fSynth->buildUserInterface(&fParameterUI);
#else
fDSP->buildUserInterface(&fStateUI);
fDSP->buildUserInterface(&fParameterUI);
#endif
startTimerHz(25);
}
FaustPlugInAudioProcessor::~FaustPlugInAudioProcessor()
{}
void FaustPlugInAudioProcessor::panic(float val, void* arg)
{
#ifdef JUCE_POLY
if (val == 1) {
static_cast<FaustSynthesiser*>(arg)->allNotesOff(0, false); // 0 stops all voices
}
#endif
}
AudioProcessor::BusesProperties FaustPlugInAudioProcessor::getBusesProperties()
{
if (PluginHostType::getPluginLoadedAs() == wrapperType_Standalone) {
if (gDSP.getNumInputs() == 0) {
return BusesProperties().withOutput("Output", AudioChannelSet::discreteChannels(std::min<int>(2, gDSP.getNumOutputs())), true);
} else {
return BusesProperties()
.withInput("Input", AudioChannelSet::discreteChannels(std::min<int>(2, gDSP.getNumInputs())), true)
.withOutput("Output", AudioChannelSet::discreteChannels(std::min<int>(2, gDSP.getNumOutputs())), true);
}
} else {
if (gDSP.getNumInputs() == 0) {
return BusesProperties().withOutput("Output", AudioChannelSet::discreteChannels(gDSP.getNumOutputs()), true);
} else {
return BusesProperties()
.withInput("Input", AudioChannelSet::discreteChannels(gDSP.getNumInputs()), true)
.withOutput("Output", AudioChannelSet::discreteChannels(gDSP.getNumOutputs()), true);
}
}
}
void FaustPlugInAudioProcessor::timerCallback()
{
GUI::updateAllGuis();
}
//==============================================================================
const String FaustPlugInAudioProcessor::getName() const
{
return JucePlugin_Name;
}
bool FaustPlugInAudioProcessor::acceptsMidi() const
{
#if JucePlugin_WantsMidiInput
return true;
#else
return false;
#endif
}
bool FaustPlugInAudioProcessor::producesMidi() const
{
#if JucePlugin_ProducesMidiOutput
return true;
#else
return false;
#endif
}
double FaustPlugInAudioProcessor::getTailLengthSeconds() const
{
return 0.0;
}
int FaustPlugInAudioProcessor::getNumPrograms()
{
return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
// so this should be at least 1, even if you're not really implementing programs.
}
int FaustPlugInAudioProcessor::getCurrentProgram()
{
return 0;
}
void FaustPlugInAudioProcessor::setCurrentProgram (int index)
{}
const String FaustPlugInAudioProcessor::getProgramName (int index)
{
return String();
}
void FaustPlugInAudioProcessor::changeProgramName (int index, const String& newName)
{}
bool FaustPlugInAudioProcessor::supportsDoublePrecisionProcessing() const
{
return sizeof(FAUSTFLOAT) == 8;
}
//==============================================================================
void FaustPlugInAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
// Use this method as the place to do any pre-playback
// initialisation that you need..
#ifdef JUCE_POLY
fSynth->setCurrentPlaybackSampleRate (sampleRate);
#else
// Possibly adapt DSP
if (fDSP->getNumInputs() > getTotalNumInputChannels() || fDSP->getNumOutputs() > getTotalNumOutputChannels()) {
fDSP = new dsp_adapter(fDSP.release(), getTotalNumInputChannels(), getTotalNumOutputChannels(), 4096);
}
fDSP->init(int(sampleRate));
#endif
}
bool FaustPlugInAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
#ifdef JUCE_POLY
return true;
#else
#if JucePlugin_IsSynth
// Stereo is supported
return (layouts.getMainOutputChannelSet().size() == 2) || (layouts.getMainOutputChannelSet().size() == fDSP->getNumOutputs());
#else
// Stereo is supported
return
((layouts.getMainInputChannelSet().size() == 2) && (layouts.getMainOutputChannelSet().size() == 2))
||
((layouts.getMainInputChannelSet().size() == fDSP->getNumInputs()) && (layouts.getMainOutputChannelSet().size() == fDSP->getNumOutputs()));
#endif
#endif
}
template <typename FloatType>
void FaustPlugInAudioProcessor::process (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages)
{
AVOIDDENORMALS;
#ifdef JUCE_POLY
fSynth->renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples());
#else
#if defined(MIDICTRL)
// Read MIDI input events from midiMessages
fMIDIHandler->decodeBuffer(midiMessages);
// Then write MIDI output events to midiMessages
fMIDIHandler->encodeBuffer(midiMessages);
#endif
// MIDI timestamp is expressed in frames
fDSP->compute(-1, buffer.getNumSamples(),
(FAUSTFLOAT**)buffer.getArrayOfReadPointers(),
(FAUSTFLOAT**)buffer.getArrayOfWritePointers());
#endif
}
//==============================================================================
bool FaustPlugInAudioProcessor::hasEditor() const
{
return true;
}
AudioProcessorEditor* FaustPlugInAudioProcessor::createEditor()
{
return new FaustPlugInAudioProcessorEditor (*this);
}
//==============================================================================
void FaustPlugInAudioProcessor::getStateInformation (MemoryBlock& destData)
{
// You should use this method to store your parameters in the memory block.
// You could do that either as raw data, or use the XML or ValueTree classes
// as intermediaries to make it easy to save and load complex data.
fStateUI.getStateInformation(destData);
}
void FaustPlugInAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
// You should use this method to restore your parameters from this memory block,
// whose contents will have been created by the getStateInformation() call.
fStateUI.setStateInformation(data, sizeInBytes);
}
//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new FaustPlugInAudioProcessor();
}
//==============================================================================
FaustPlugInAudioProcessorEditor::FaustPlugInAudioProcessorEditor (FaustPlugInAudioProcessor& p)
: AudioProcessorEditor (&p), processor (p)
{
addAndMakeVisible(juceGUI);
#ifdef JUCE_POLY
p.fSynth->buildUserInterface(&juceGUI);
#else
p.fDSP->buildUserInterface(&juceGUI);
#endif
juce::Rectangle<int> recommendedSize = juceGUI.getSize();
setSize (recommendedSize.getWidth(), recommendedSize.getHeight());
}
FaustPlugInAudioProcessorEditor::~FaustPlugInAudioProcessorEditor()
{}
//==============================================================================
void FaustPlugInAudioProcessorEditor::paint (Graphics& g)
{
g.fillAll (Colours::white);
}
void FaustPlugInAudioProcessorEditor::resized()
{
juceGUI.setBounds(getLocalBounds());
}
// Globals
std::list<GUI*> GUI::fGuiList;
ztimedmap GUI::gTimedZoneMap;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment