-
-
Save xaviergodart/66e3b7ac9483735bb8ec1bba82ec923b to your computer and use it in GitHub Desktop.
GainCtl Faust generated juce cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//---------------------------------------------------------- | |
// 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