--- /dev/null
+/************************************************************************
+
+ 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.
+
+
+ ************************************************************************
+ ************************************************************************/
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <math.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/soundcard.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <gtk/gtk.h>
+#include <pthread.h>
+#include <sys/wait.h>
+
+#include <map>
+#include <list>
+#include <vector>
+
+#include <iostream>
+#include <fstream>
+
+
+using namespace std;
+
+struct Meta : map<const char*, const char*>
+{
+ void declare (const char* key, const char* value) { (*this)[key]=value; }
+};
+
+
+// g++ -O3 -lm -lpthread `gtk-config --cflags --libs` ex2.cpp
+
+
+// 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
+
+//#define BENCHMARKMODE
+
+//-------------------------------------------------------------------
+// Generic min and max using c++ inline
+//-------------------------------------------------------------------
+
+inline int max (unsigned int a, unsigned int b) { return (a>b) ? a : b; }
+inline int max (int a, int b) { return (a>b) ? a : b; }
+
+inline long max (long a, long b) { return (a>b) ? a : b; }
+inline long max (int a, long b) { return (a>b) ? a : b; }
+inline long max (long a, int b) { return (a>b) ? a : b; }
+
+inline float max (float a, float b) { return (a>b) ? a : b; }
+inline float max (int a, float b) { return (a>b) ? a : b; }
+inline float max (float a, int b) { return (a>b) ? a : b; }
+inline float max (long a, float b) { return (a>b) ? a : b; }
+inline float max (float a, long b) { return (a>b) ? a : b; }
+
+inline double max (double a, double b) { return (a>b) ? a : b; }
+inline double max (int a, double b) { return (a>b) ? a : b; }
+inline double max (double a, int b) { return (a>b) ? a : b; }
+inline double max (long a, double b) { return (a>b) ? a : b; }
+inline double max (double a, long b) { return (a>b) ? a : b; }
+inline double max (float a, double b) { return (a>b) ? a : b; }
+inline double max (double a, float b) { return (a>b) ? a : b; }
+
+
+inline int min (int a, int b) { return (a<b) ? a : b; }
+
+inline long min (long a, long b) { return (a<b) ? a : b; }
+inline long min (int a, long b) { return (a<b) ? a : b; }
+inline long min (long a, int b) { return (a<b) ? a : b; }
+
+inline float min (float a, float b) { return (a<b) ? a : b; }
+inline float min (int a, float b) { return (a<b) ? a : b; }
+inline float min (float a, int b) { return (a<b) ? a : b; }
+inline float min (long a, float b) { return (a<b) ? a : b; }
+inline float min (float a, long b) { return (a<b) ? a : b; }
+
+inline double min (double a, double b) { return (a<b) ? a : b; }
+inline double min (int a, double b) { return (a<b) ? a : b; }
+inline double min (double a, int b) { return (a<b) ? a : b; }
+inline double min (long a, double b) { return (a<b) ? a : b; }
+inline double min (double a, long b) { return (a<b) ? a : b; }
+inline double min (float a, double b) { return (a<b) ? a : b; }
+inline double min (double a, float b) { return (a<b) ? a : b; }
+
+
+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; }
+
+
+void setRealtimePriority ()
+{
+ struct passwd * pw;
+ int err;
+ uid_t uid;
+ struct sched_param param;
+
+ uid = getuid ();
+ pw = getpwnam ("root");
+ setuid (pw->pw_uid);
+ param.sched_priority = 50; /* 0 to 99 */
+ err = sched_setscheduler(0, SCHED_RR, ¶m);
+ setuid (uid);
+ if (err != -1) {
+ printf("OK : Running with realtime priority\n");
+ } else {
+ printf("Warning : running with non-realtime priority\n");
+ }
+
+}
+
+
+
+/******************************************************************************
+*******************************************************************************
+
+ VECTOR INTRINSICS
+
+*******************************************************************************
+*******************************************************************************/
+
+//inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((unsigned)(calloc((nmemb*size)+15,sizeof(char)))+15 & 0xfffffff0); }
+//inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((size_t)(calloc((nmemb*size)+15,sizeof(char)))+15 & ~15); }
+
+
+<<includeIntrinsic>>
+
+#ifdef BENCHMARKMODE
+// mesuring jack performances
+static __inline__ unsigned long long int rdtsc(void)
+{
+ unsigned long long int x;
+ __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
+ return x;
+}
+
+#define KSKIP 10
+#define KMESURE 1024
+int mesure = 0;
+unsigned long long int starts[KMESURE];
+unsigned long long int stops [KMESURE];
+
+#define STARTMESURE starts[mesure%KMESURE] = rdtsc();
+#define STOPMESURE stops[mesure%KMESURE] = rdtsc(); mesure = mesure+1;
+
+void printstats()
+{
+ unsigned long long int low, hi, tot;
+ low = hi = tot = (stops[KSKIP] - starts[KSKIP]);
+
+ if (mesure < KMESURE) {
+
+ for (int i = KSKIP+1; i<mesure; i++) {
+ unsigned long long int m = stops[i] - starts[i];
+ if (m<low) low = m;
+ if (m>hi) hi = m;
+ tot += m;
+ }
+ cout << low << ' ' << tot/(mesure-KSKIP) << ' ' << hi << endl;
+
+ } else {
+
+ for (int i = KSKIP+1; i<KMESURE; i++) {
+ unsigned long long int m = stops[i] - starts[i];
+ if (m<low) low = m;
+ if (m>hi) hi = m;
+ tot += m;
+ }
+ cout << low << ' ' << tot/(KMESURE-KSKIP) << ' ' << hi << endl;
+
+ }
+}
+#else
+
+#define STARTMESURE
+#define STOPMESURE
+
+#endif
+
+
+/******************************************************************************
+*******************************************************************************
+
+ AUDIO INTERFACE
+
+*******************************************************************************
+*******************************************************************************/
+
+enum { kRead = 1, kWrite = 2, kReadWrite = 3 };
+
+// AudioParam : a convenient class to pass parameters to the AudioInterface
+struct AudioParam
+{
+ const char* fDeviceName;
+ int fSamplingFrequency;
+ int fRWMode;
+ int fSampleFormat;
+ int fFramesPerBuffer;
+
+ AudioParam() :
+ fDeviceName("/dev/dsp"),
+ fSamplingFrequency(44100),
+ fRWMode(kReadWrite),
+ fSampleFormat(AFMT_S16_LE),
+ fFramesPerBuffer(512)
+ {}
+
+ AudioParam& device(const char * n) { fDeviceName = n; return *this; }
+ AudioParam& frequency(int f) { fSamplingFrequency = f; return *this; }
+ AudioParam& mode(int m) { fRWMode = m; return *this; }
+ AudioParam& format(int f) { fSampleFormat = f; return *this; }
+ AudioParam& buffering(int fpb) { fFramesPerBuffer = fpb; return *this; }
+};
+
+class AudioInterface
+{
+ private :
+ AudioParam fParam;
+ int fOutputDevice ;
+ int fInputDevice ;
+ int fNumOfOutputChannels;
+ int fNumOfInputChannels;
+ int fInputBufferSize;
+ short* fInputBuffer;
+ int fOutputBufferSize;
+ short* fOutputBuffer;
+
+
+ public :
+
+ const char* getDeviceName() { return fParam.fDeviceName; }
+ int getSamplingFrequency() { return fParam.fSamplingFrequency; }
+ int getRWMode() { return fParam.fRWMode; }
+ int getSampleFormat() { return fParam.fSampleFormat; }
+ int getFramesPerBuffer() { return fParam.fFramesPerBuffer; }
+
+ int getNumOutputs() { return fNumOfOutputChannels; }
+ int getNumInputs() { return fNumOfInputChannels; }
+ int getInputBufferSize() { return fInputBufferSize; }
+ int getOutputBufferSize() { return fOutputBufferSize; }
+
+
+ AudioInterface(const AudioParam& ap = AudioParam()) : fParam(ap)
+ {
+ fOutputDevice = -1;
+ fInputDevice = -1;
+ fNumOfOutputChannels = 0;
+ fNumOfInputChannels = 0;
+ fInputBufferSize = 0;
+ fInputBuffer = 0;
+ fOutputBufferSize = 0;
+ fOutputBuffer = 0;
+ }
+
+
+ void openInputAudioDev ()
+ {
+ assert( (fInputDevice = ::open(fParam.fDeviceName, O_RDONLY, 0)) > 0);
+ assert( ioctl(fInputDevice, SNDCTL_DSP_SETFMT, &fParam.fSampleFormat) != -1);
+ assert( ioctl(fInputDevice, SNDCTL_DSP_CHANNELS, &fNumOfInputChannels) != -1);
+ assert( ioctl(fInputDevice, SNDCTL_DSP_SPEED, &fParam.fSamplingFrequency) != -1);
+
+ int gFragFormat = (1 << 16) + int2pow2(fParam.fFramesPerBuffer * 2 * fNumOfInputChannels);
+ assert( ioctl(fInputDevice, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) != -1);
+
+ fInputBufferSize = 0;
+ assert( ioctl(fInputDevice, SNDCTL_DSP_GETBLKSIZE, &fInputBufferSize) != -1);
+ assert( fInputBufferSize == fParam.fFramesPerBuffer * 2 * fNumOfInputChannels );
+
+ fInputBuffer = (short*) calloc(fInputBufferSize, 1);
+ }
+
+
+ void openOutputAudioDev ()
+ {
+ assert( (fOutputDevice = ::open(fParam.fDeviceName, O_WRONLY, 0)) > 0);
+ assert( ioctl(fOutputDevice, SNDCTL_DSP_SETFMT, &fParam.fSampleFormat) != -1);
+ assert( ioctl(fOutputDevice, SNDCTL_DSP_CHANNELS,&fNumOfOutputChannels)!= -1);
+ assert( ioctl(fOutputDevice, SNDCTL_DSP_SPEED, &fParam.fSamplingFrequency) != -1);
+
+ int gFragFormat = (1 << 16) + int2pow2(fParam.fFramesPerBuffer * 2 * fNumOfOutputChannels);
+ assert( ioctl(fOutputDevice, SNDCTL_DSP_SETFRAGMENT, &gFragFormat) != -1);
+
+ fOutputBufferSize = 0;
+ assert( ioctl(fOutputDevice, SNDCTL_DSP_GETBLKSIZE, &fOutputBufferSize) != -1);
+ assert( fOutputBufferSize == fParam.fFramesPerBuffer * 2 * fNumOfOutputChannels );
+
+ fOutputBuffer = (short*)calloc(fOutputBufferSize, 1);
+ }
+
+
+ void open()
+ {
+ if (fParam.fRWMode & kRead) openInputAudioDev();
+ if (fParam.fRWMode & kWrite) openOutputAudioDev();
+ }
+
+
+ void close()
+ {
+ if (fParam.fRWMode & kRead) ::close(fOutputDevice);
+ if (fParam.fRWMode & kWrite) ::close(fInputDevice);
+ }
+
+
+
+ //----------------------------------------------------------------
+ // allocChanGroup() : allocate a group of audio buffers
+ // chan[] : is an array of buffer pointers
+ // n : is the number of buffers to allocate
+ // len : is the length of each buffer
+ //----------------------------------------------------------------
+
+ void allocChanGroup(float* chan[], int n, int len)
+ {
+ for (int c = 0; c < n; c++) {
+ chan[c] = (float*) calloc (len, sizeof(float));
+ }
+ }
+
+
+ //----------------------------------------------------------------
+ // info() : print information on the audio device
+ //----------------------------------------------------------------
+
+ void info()
+ {
+ audio_buf_info info;
+ int cap;
+ printf("Audio Interface Description :\n");
+ printf("Sampling Frequency : %d, Sample Format : %d, Mode : %d\n", getSamplingFrequency(), getSampleFormat(), getRWMode());
+
+ if (getRWMode() & kWrite) {
+ assert( ioctl(fOutputDevice, SNDCTL_DSP_GETOSPACE, &info) != -1);
+ printf("output space info: fragments=%d, fragstotal=%d, fragsize=%d, bytes=%d\n", info.fragments, info.fragstotal,
+ info.fragsize, info.bytes);
+
+ assert( ioctl(fOutputDevice,SNDCTL_DSP_GETCAPS, &cap) != -1);
+ printf("Output capabilities - %d channels : ", fNumOfOutputChannels);
+
+ if (cap & DSP_CAP_DUPLEX) printf(" DSP_CAP_DUPLEX");
+ if (cap & DSP_CAP_REALTIME) printf(" DSP_CAP_REALTIME");
+ if (cap & DSP_CAP_DUPLEX) printf(" DSP_CAP_DUPLEX");
+ if (cap & DSP_CAP_BATCH) printf(" DSP_CAP_BATCH");
+ if (cap & DSP_CAP_COPROC) printf(" DSP_CAP_COPROC");
+ if (cap & DSP_CAP_TRIGGER) printf(" DSP_CAP_TRIGGER");
+ if (cap & DSP_CAP_MMAP) printf(" DSP_CAP_MMAP");
+ if (cap & DSP_CAP_MULTI) printf(" DSP_CAP_MULTI");
+ if (cap & DSP_CAP_BIND) printf(" DSP_CAP_BIND");
+ printf("\n");
+ printf("Output block size = %d\n", fOutputBufferSize);
+ }
+
+
+ if (getRWMode() & kRead) {
+ assert( ioctl(fInputDevice, SNDCTL_DSP_GETISPACE, &info) != -1);
+ printf("input space info: fragments=%d, fragstotal=%d, fragsize=%d, bytes=%d\n", info.fragments, info.fragstotal,
+ info.fragsize, info.bytes);
+
+
+ assert( ioctl(fInputDevice,SNDCTL_DSP_GETCAPS, &cap) != -1);
+ printf("Input capabilities - %d channels : ", fNumOfInputChannels);
+ if (cap & DSP_CAP_DUPLEX) printf(" DSP_CAP_DUPLEX");
+ if (cap & DSP_CAP_REALTIME) printf(" DSP_CAP_REALTIME");
+ if (cap & DSP_CAP_DUPLEX) printf(" DSP_CAP_DUPLEX");
+ if (cap & DSP_CAP_BATCH) printf(" DSP_CAP_BATCH");
+ if (cap & DSP_CAP_COPROC) printf(" DSP_CAP_COPROC");
+ if (cap & DSP_CAP_TRIGGER) printf(" DSP_CAP_TRIGGER");
+ if (cap & DSP_CAP_MMAP) printf(" DSP_CAP_MMAP");
+ if (cap & DSP_CAP_MULTI) printf(" DSP_CAP_MULTI");
+ if (cap & DSP_CAP_BIND) printf(" DSP_CAP_BIND");
+ printf("\n");
+ printf("Input block size = %d\n", fInputBufferSize);
+ }
+ }
+
+
+ //----------------------------------------------------------------
+ // read() : read
+ //----------------------------------------------------------------
+
+ bool read(int frames, float* channel[])
+ {
+ int bytes = frames * 2 * fNumOfInputChannels; assert(bytes <= fInputBufferSize);
+ int count = ::read(fInputDevice, fInputBuffer, bytes);
+ //assert (bytes == count);
+
+ for (int s = 0; s < frames; s++) {
+ for (int c = 0; c < fNumOfInputChannels; c++) {
+ channel[c][s] = float(fInputBuffer[c + s*fNumOfInputChannels])*(1.0/float(SHRT_MAX));
+ }
+ }
+ return bytes == count;
+ }
+
+
+ bool write(int frames, float* channel[])
+ {
+ int bytes = frames * 2 * fNumOfOutputChannels; assert(bytes <= fOutputBufferSize);
+
+ for (int f = 0; f < frames; f++) {
+ for (int c = 0; c < fNumOfOutputChannels; c++) {
+ float x = channel[c][f];
+ fOutputBuffer[c + f*fNumOfOutputChannels] = short( max(min(x,1.0),-1.0) * float(SHRT_MAX) ) ;
+ }
+ }
+
+ int count = ::write(fOutputDevice, fOutputBuffer, bytes);
+ assert (bytes == count);
+
+ return bytes == count;
+ }
+
+
+
+};
+
+
+
+/******************************************************************************
+*******************************************************************************
+
+ GRAPHIC USER INTERFACE (v2)
+ abstract interfaces
+
+*******************************************************************************
+*******************************************************************************/
+
+#include <map>
+#include <list>
+
+using namespace std;
+
+
+struct uiItem;
+typedef void (*uiCallback)(float val, void* data);
+
+/**
+ * Graphic User Interface : abstract definition
+ */
+
+class UI
+{
+ typedef list<uiItem*> clist;
+ typedef map<float*, clist*> zmap;
+
+ private:
+ static list<UI*> fGuiList;
+ zmap fZoneMap;
+ bool fStopped;
+
+ public:
+
+ UI() : fStopped(false) {
+ fGuiList.push_back(this);
+ }
+
+ virtual ~UI() {
+ // suppression de this dans fGuiList
+ }
+
+ // -- registerZone(z,c) : zone management
+
+ void registerZone(float* z, uiItem* c)
+ {
+ if (fZoneMap.find(z) == fZoneMap.end()) fZoneMap[z] = new clist();
+ fZoneMap[z]->push_back(c);
+ }
+
+ // -- saveState(filename) : save the value of every zone to a file
+
+ void saveState(char* filename)
+ {
+ ofstream f(filename);
+
+ for (zmap::iterator i=fZoneMap.begin(); i!=fZoneMap.end(); i++) {
+ f << *(i->first) << ' ';
+ }
+
+ f << endl;
+ f.close();
+ }
+
+ // -- recallState(filename) : load the value of every zone from a file
+
+ void recallState(char* filename)
+ {
+ ifstream f(filename);
+ if (f.good()) {
+ for (zmap::iterator i=fZoneMap.begin(); i!=fZoneMap.end(); i++) {
+ f >> *(i->first);
+ }
+ }
+ f.close();
+ }
+
+ void updateAllZones();
+
+ void updateZone(float* z);
+
+ static void updateAllGuis()
+ {
+ list<UI*>::iterator g;
+ for (g = fGuiList.begin(); g != fGuiList.end(); g++) {
+ (*g)->updateAllZones();
+ }
+ }
+
+ // -- active widgets
+
+ virtual void addButton(const char* label, float* zone) = 0;
+ virtual void addToggleButton(const char* label, float* zone) = 0;
+ virtual void addCheckButton(const char* label, float* zone) = 0;
+ virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step) = 0;
+ virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step) = 0;
+ virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step) = 0;
+
+ // -- passive widgets
+
+ virtual void addNumDisplay(const char* label, float* zone, int precision) = 0;
+ virtual void addTextDisplay(const char* label, float* zone, const char* names[], float min, float max) = 0;
+ virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max) = 0;
+ virtual void addVerticalBargraph(const char* label, float* zone, float min, float max) = 0;
+
+ void addCallback(float* zone, uiCallback foo, void* data);
+
+ // -- widget's layouts
+
+ virtual void openFrameBox(const char* label) = 0;
+ 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;
+
+ virtual void show() = 0;
+ virtual void run() = 0;
+
+ void stop() { fStopped = true; }
+ bool stopped() { return fStopped; }
+
+ virtual void declare(float* zone, const char* key, const char* value) {}
+};
+
+
+/**
+ * User Interface Item: abstract definition
+ */
+
+class uiItem
+{
+ protected :
+
+ UI* fGUI;
+ float* fZone;
+ float fCache;
+
+ uiItem (UI* ui, float* zone) : fGUI(ui), fZone(zone), fCache(-123456.654321)
+ {
+ ui->registerZone(zone, this);
+ }
+
+
+ public :
+ virtual ~uiItem() {}
+
+ void modifyZone(float v)
+ {
+ fCache = v;
+ if (*fZone != v) {
+ *fZone = v;
+ fGUI->updateZone(fZone);
+ }
+ }
+
+ float cache() { return fCache; }
+ virtual void reflectZone() = 0;
+};
+
+
+/**
+ * Callback Item
+ */
+
+struct uiCallbackItem : public uiItem
+{
+ uiCallback fCallback;
+ void* fData;
+
+ uiCallbackItem(UI* ui, float* zone, uiCallback foo, void* data)
+ : uiItem(ui, zone), fCallback(foo), fData(data) {}
+
+ virtual void reflectZone() {
+ float v = *fZone;
+ fCache = v;
+ fCallback(v, fData);
+ }
+};
+
+// en cours d'installation de call back. a finir!!!!!
+
+/**
+ * Update all user items reflecting zone z
+ */
+
+inline void UI::updateZone(float* z)
+{
+ float 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 UI::updateAllZones()
+{
+ for (zmap::iterator m = fZoneMap.begin(); m != fZoneMap.end(); m++) {
+ float* z = m->first;
+ clist* l = m->second;
+ float v = *z;
+ for (clist::iterator c = l->begin(); c != l->end(); c++) {
+ if ((*c)->cache() != v) (*c)->reflectZone();
+ }
+ }
+}
+
+inline void UI::addCallback(float* zone, uiCallback foo, void* data)
+{
+ new uiCallbackItem(this, zone, foo, data);
+};
+
+/******************************************************************************
+*******************************************************************************
+
+ GRAPHIC USER INTERFACE
+ gtk interface
+
+*******************************************************************************
+*******************************************************************************/
+
+#include <gtk/gtk.h>
+
+#define stackSize 256
+
+// Insertion modes
+
+#define kSingleMode 0
+#define kBoxMode 1
+#define kTabMode 2
+
+
+/**
+ * rmWhiteSpaces(): Remove the leading and trailing white spaces of a string
+ * (but not those in the middle of the string)
+ */
+static string rmWhiteSpaces(const string& s)
+{
+ size_t i = s.find_first_not_of(" \t");
+ size_t j = s.find_last_not_of(" \t");
+
+ if (i != string::npos & j != string::npos) {
+ return s.substr(i, 1+j-i);
+ } else {
+ return "";
+ }
+}
+
+
+/**
+ * Extracts metdata from a label : 'vol [unit: dB]' -> 'vol' + metadata
+ */
+static void extractMetadata(const string& fulllabel, string& label, map<string, string>& metadata)
+{
+ enum {kLabel, kEscape1, kEscape2, kEscape3, kKey, kValue};
+ int state = kLabel; int deep = 0;
+ 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 :
+ cerr << "ERROR unrecognized state " << state << endl;
+ }
+ }
+ label = rmWhiteSpaces(label);
+}
+
+
+class GTKUI : public UI
+{
+ private :
+ static bool fInitialized;
+ static list<UI*> fGuiList;
+ static map<float*, float> fGuiSize; // map widget zone with widget size coef
+ static map<float*, string> fTooltip; // map widget zone with tooltip strings
+
+ protected :
+ GtkWidget* fWindow;
+ int fTop;
+ GtkWidget* fBox[stackSize];
+ int fMode[stackSize];
+ bool fStopped;
+
+ GtkWidget* addWidget(const char* label, GtkWidget* w);
+ virtual void pushBox(int mode, GtkWidget* w);
+
+
+ public :
+
+ static const gboolean expand = TRUE;
+ static const gboolean fill = TRUE;
+ static const gboolean homogene = FALSE;
+
+ GTKUI(char * name, int* pargc, char*** pargv);
+
+ // -- Labels and metadata
+
+ virtual void declare (float* zone, const char* key, const char* value);
+ virtual int checkLabelOptions (GtkWidget* widget, const string& fullLabel, string& simplifiedLabel);
+ virtual void checkForTooltip (float* zone, GtkWidget* widget);
+
+ // -- layout groups
+
+ virtual void openFrameBox(const char* label);
+ virtual void openTabBox(const char* label = "");
+ virtual void openHorizontalBox(const char* label = "");
+ virtual void openVerticalBox(const char* label = "");
+
+ // -- extra widget's layouts
+
+ virtual void openDialogBox(const char* label, float* zone);
+ virtual void openEventBox(const char* label = "");
+ virtual void openHandleBox(const char* label = "");
+ virtual void openExpanderBox(const char* label, float* zone);
+
+ virtual void closeBox();
+ virtual void adjustStack(int n);
+
+ // -- active widgets
+
+ virtual void addButton(const char* label, float* zone);
+ virtual void addToggleButton(const char* label, float* zone);
+ virtual void addCheckButton(const char* label, float* zone);
+ virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step);
+ virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step);
+ virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step);
+
+ // -- passive display widgets
+
+ virtual void addNumDisplay(const char* label, float* zone, int precision);
+ virtual void addTextDisplay(const char* label, float* zone, const char* names[], float min, float max);
+ virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max);
+ virtual void addVerticalBargraph(const char* label, float* zone, float min, float max);
+
+ virtual void show();
+ virtual void run();
+
+};
+
+
+
+/******************************************************************************
+*******************************************************************************
+
+ GRAPHIC USER INTERFACE (v2)
+ gtk implementation
+
+*******************************************************************************
+*******************************************************************************/
+
+// global static fields
+
+list<UI*> UI::fGuiList;
+
+bool GTKUI::fInitialized = false;
+map<float*, float> GTKUI::fGuiSize;
+map<float*, string> GTKUI::fTooltip;
+
+
+
+static gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data )
+{
+ return FALSE;
+}
+
+static void destroy_event( GtkWidget *widget, gpointer data )
+{
+ gtk_main_quit ();
+}
+
+
+GTKUI::GTKUI(char * name, int* pargc, char*** pargv)
+{
+ if (!fInitialized) {
+ gtk_init(pargc, pargv);
+ fInitialized = true;
+ }
+
+ fWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ //gtk_container_set_border_width (GTK_CONTAINER (fWindow), 10);
+ gtk_window_set_title (GTK_WINDOW (fWindow), name);
+ gtk_signal_connect (GTK_OBJECT (fWindow), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL);
+ gtk_signal_connect (GTK_OBJECT (fWindow), "destroy", GTK_SIGNAL_FUNC (destroy_event), NULL);
+
+ fTop = 0;
+ fBox[fTop] = gtk_vbox_new (homogene, 4);
+ fMode[fTop] = kBoxMode;
+ gtk_container_add (GTK_CONTAINER (fWindow), fBox[fTop]);
+ fStopped = false;
+}
+
+// empilement des boites
+
+void GTKUI::pushBox(int mode, GtkWidget* w)
+{
+ ++fTop;
+ assert(fTop < stackSize);
+ fMode[fTop] = mode;
+ fBox[fTop] = w;
+}
+
+
+/**
+ * Remove n levels from the stack S before the top level
+ * adjustStack(n): S -> S' with S' = S(0),S(n+1),S(n+2),...
+ */
+void GTKUI::adjustStack(int n)
+{
+ if (n > 0) {
+ assert(fTop >= n);
+
+ fTop -= n;
+ fMode[fTop] = fMode[fTop+n];
+ fBox[fTop] = fBox[fTop+n];
+ }
+}
+
+void GTKUI::closeBox()
+{
+ --fTop;
+ assert(fTop >= 0);
+}
+
+
+/**
+ * Analyses the widget zone metadata declarations and takes
+ * appropriate actions
+ */
+void GTKUI::declare(float* zone, const char* key, const char* value)
+{
+ if (strcmp(key,"size")==0) {
+ fGuiSize[zone]=atof(value);
+ }
+ else if (strcmp(key,"tooltip")==0) {
+ fTooltip[zone] = value ;
+ }
+}
+
+
+
+/**
+ * Analyses a full label and activates the relevant options. returns a simplified
+ * label (without options) and an amount of stack adjustement (in case additional
+ * containers were pushed on the stack).
+ */
+
+int GTKUI::checkLabelOptions(GtkWidget* widget, const string& fullLabel, string& simplifiedLabel)
+{
+ map<string, string> metadata;
+ extractMetadata(fullLabel, simplifiedLabel, metadata);
+
+ if (metadata.count("tooltip")) {
+ gtk_tooltips_set_tip (gtk_tooltips_new (), widget, metadata["tooltip"].c_str(), NULL);
+ }
+ if (metadata["option"] == "detachable") {
+ openHandleBox(simplifiedLabel.c_str());
+ return 1;
+ }
+
+ // no adjustement of the stack needed
+ return 0;
+}
+
+/**
+ * Check if a tooltip is associated to a zone and add it to the corresponding widget
+ */
+void GTKUI::checkForTooltip(float* zone, GtkWidget* widget)
+{
+ if (fTooltip.count(zone)) {
+ gtk_tooltips_set_tip (gtk_tooltips_new (), widget, fTooltip[zone].c_str(), NULL);
+ }
+}
+
+
+// les differentes boites
+
+void GTKUI::openFrameBox(const char* label)
+{
+ GtkWidget * box = gtk_frame_new (label);
+ //gtk_container_set_border_width (GTK_CONTAINER (box), 10);
+
+ pushBox(kSingleMode, addWidget(label, box));
+}
+
+
+void GTKUI::openTabBox(const char* fullLabel)
+{
+ string label;
+ GtkWidget* widget = gtk_notebook_new();
+
+ int adjust = checkLabelOptions(widget, fullLabel, label);
+
+ pushBox(kTabMode, addWidget(label.c_str(), widget));
+
+ // adjust stack because otherwise Handlebox will remain open
+ adjustStack(adjust);
+}
+
+
+void GTKUI::openHorizontalBox(const char* fullLabel)
+{
+ string label;
+ GtkWidget* box = gtk_hbox_new (homogene, 4);
+ int adjust = checkLabelOptions(box, fullLabel, label);
+
+ gtk_container_set_border_width (GTK_CONTAINER (box), 10);
+
+ if (fMode[fTop] != kTabMode && label[0] != 0) {
+ GtkWidget * frame = addWidget(label.c_str(), gtk_frame_new (label.c_str()));
+ gtk_container_add (GTK_CONTAINER(frame), box);
+ gtk_widget_show(box);
+ pushBox(kBoxMode, box);
+ } else {
+ pushBox(kBoxMode, addWidget(label.c_str(), box));
+ }
+
+ // adjust stack because otherwise Handlebox will remain open
+ adjustStack(adjust);
+}
+
+
+void GTKUI::openVerticalBox(const char* fullLabel)
+{
+ string label;
+ GtkWidget * box = gtk_vbox_new (homogene, 4);
+ int adjust = checkLabelOptions(box, fullLabel, label);
+
+ gtk_container_set_border_width (GTK_CONTAINER (box), 10);
+
+ if (fMode[fTop] != kTabMode && label[0] != 0) {
+ GtkWidget * frame = addWidget(label.c_str(), gtk_frame_new (label.c_str()));
+ gtk_container_add (GTK_CONTAINER(frame), box);
+ gtk_widget_show(box);
+ pushBox(kBoxMode, box);
+ } else {
+ pushBox(kBoxMode, addWidget(label.c_str(), box));
+ }
+
+ // adjust stack because otherwise Handlebox will remain open
+ adjustStack(adjust);
+}
+
+
+void GTKUI::openHandleBox(const char* label)
+{
+ GtkWidget * box = gtk_hbox_new (homogene, 4);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+ if (fMode[fTop] != kTabMode && label[0] != 0)
+ {
+ GtkWidget * frame = addWidget(label, gtk_handle_box_new ());
+ gtk_container_add (GTK_CONTAINER(frame), box);
+ gtk_widget_show(box);
+ pushBox(kBoxMode, box);
+ }
+ else
+ {
+ pushBox(kBoxMode, addWidget(label, box));
+ }
+}
+
+
+void GTKUI::openEventBox(const char* label)
+{
+ GtkWidget * box = gtk_hbox_new (homogene, 4);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+ if (fMode[fTop] != kTabMode && label[0] != 0)
+ {
+ GtkWidget * frame = addWidget(label, gtk_event_box_new ());
+ gtk_container_add (GTK_CONTAINER(frame), box);
+ gtk_widget_show(box);
+ pushBox(kBoxMode, box);
+ }
+ else
+ {
+ pushBox(kBoxMode, addWidget(label, box));
+ }
+}
+
+
+struct uiExpanderBox : public uiItem
+{
+ GtkExpander* fButton;
+ uiExpanderBox(UI* ui, float* zone, GtkExpander* b) : uiItem(ui, zone), fButton(b) {}
+ static void expanded (GtkWidget *widget, gpointer data)
+ {
+ float v = gtk_expander_get_expanded (GTK_EXPANDER(widget));
+ if (v == 1.000000)
+ {
+ v = 0;
+ }
+ else v = 1;
+ ((uiItem*)data)->modifyZone(v);
+ }
+
+ virtual void reflectZone()
+ {
+ float v = *fZone;
+ fCache = v;
+ gtk_expander_set_expanded(GTK_EXPANDER(fButton), v);
+ }
+};
+
+void GTKUI::openExpanderBox(const char* label, float* zone)
+{
+ *zone = 0.0;
+ GtkWidget * box = gtk_hbox_new (homogene, 4);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+ if (fMode[fTop] != kTabMode && label[0] != 0)
+ {
+ GtkWidget * frame = addWidget(label, gtk_expander_new (label));
+ gtk_container_add (GTK_CONTAINER(frame), box);
+ uiExpanderBox* c = new uiExpanderBox(this, zone, GTK_EXPANDER(frame));
+ gtk_signal_connect (GTK_OBJECT (frame), "activate", GTK_SIGNAL_FUNC (uiExpanderBox::expanded), (gpointer)c);
+ gtk_widget_show(box);
+ pushBox(kBoxMode, box);
+ }
+ else
+ {
+ pushBox(kBoxMode, addWidget(label, box));
+ }
+}
+
+
+
+GtkWidget* GTKUI::addWidget(const char* label, GtkWidget* w)
+{
+ switch (fMode[fTop]) {
+ case kSingleMode : gtk_container_add (GTK_CONTAINER(fBox[fTop]), w); break;
+ case kBoxMode : gtk_box_pack_start (GTK_BOX(fBox[fTop]), w, expand, fill, 0); break;
+ case kTabMode : gtk_notebook_append_page (GTK_NOTEBOOK(fBox[fTop]), w, gtk_label_new(label)); break;
+ }
+ gtk_widget_show (w);
+ return w;
+}
+
+// --------------------------- Press button ---------------------------
+
+struct uiButton : public uiItem
+{
+ GtkButton* fButton;
+
+ uiButton (UI* ui, float* zone, GtkButton* b) : uiItem(ui, zone), fButton(b) {}
+
+ static void pressed( GtkWidget *widget, gpointer data )
+ {
+ uiItem* c = (uiItem*) data;
+ c->modifyZone(1.0);
+ }
+
+ static void released( GtkWidget *widget, gpointer data )
+ {
+ uiItem* c = (uiItem*) data;
+ c->modifyZone(0.0);
+ }
+
+ virtual void reflectZone()
+ {
+ float v = *fZone;
+ fCache = v;
+ if (v > 0.0) gtk_button_pressed(fButton); else gtk_button_released(fButton);
+ }
+};
+
+void GTKUI::addButton(const char* label, float* zone)
+{
+ *zone = 0.0;
+ GtkWidget* button = gtk_button_new_with_label (label);
+ addWidget(label, button);
+
+ uiButton* c = new uiButton(this, zone, GTK_BUTTON(button));
+
+ gtk_signal_connect (GTK_OBJECT (button), "pressed", GTK_SIGNAL_FUNC (uiButton::pressed), (gpointer) c);
+ gtk_signal_connect (GTK_OBJECT (button), "released", GTK_SIGNAL_FUNC (uiButton::released), (gpointer) c);
+
+ checkForTooltip(zone, button);
+}
+
+// --------------------------- Toggle Buttons ---------------------------
+
+struct uiToggleButton : public uiItem
+{
+ GtkToggleButton* fButton;
+
+ uiToggleButton(UI* ui, float* zone, GtkToggleButton* b) : uiItem(ui, zone), fButton(b) {}
+
+ static void toggled (GtkWidget *widget, gpointer data)
+ {
+ float v = (GTK_TOGGLE_BUTTON (widget)->active) ? 1.0 : 0.0;
+ ((uiItem*)data)->modifyZone(v);
+ }
+
+ virtual void reflectZone()
+ {
+ float v = *fZone;
+ fCache = v;
+ gtk_toggle_button_set_active(fButton, v > 0.0);
+ }
+};
+
+void GTKUI::addToggleButton(const char* label, float* zone)
+{
+ *zone = 0.0;
+ GtkWidget* button = gtk_toggle_button_new_with_label (label);
+ addWidget(label, button);
+
+ uiToggleButton* c = new uiToggleButton(this, zone, GTK_TOGGLE_BUTTON(button));
+ gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (uiToggleButton::toggled), (gpointer) c);
+
+ checkForTooltip(zone, button);
+}
+
+
+
+void show_dialog(GtkWidget *widget, gpointer data)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)) == TRUE)
+ {
+ gtk_widget_show(GTK_WIDGET(data));
+ gint root_x, root_y;
+ gtk_window_get_position (GTK_WINDOW(data), &root_x, &root_y);
+ root_y -= 120;
+ gtk_window_move(GTK_WINDOW(data), root_x, root_y);
+ }
+ else gtk_widget_hide(GTK_WIDGET(data));
+}
+
+static gboolean deleteevent( GtkWidget *widget, gpointer data )
+{
+ return TRUE;
+}
+
+void GTKUI::openDialogBox(const char* label, float* zone)
+{
+ // create toplevel window and set properties
+ GtkWidget * dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_decorated(GTK_WINDOW(dialog), TRUE);
+ gtk_window_set_deletable(GTK_WINDOW(dialog), FALSE);
+ gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
+ gtk_window_set_gravity(GTK_WINDOW(dialog), GDK_GRAVITY_SOUTH);
+ gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(fWindow));
+ gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
+ gtk_window_set_keep_below (GTK_WINDOW(dialog), FALSE);
+ gtk_window_set_title (GTK_WINDOW (dialog), label);
+ g_signal_connect (G_OBJECT (dialog), "delete_event", G_CALLBACK (deleteevent), NULL);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
+
+ GtkWidget * box = gtk_hbox_new (homogene, 4);
+
+ *zone = 0.0;
+ GtkWidget* button = gtk_toggle_button_new ();
+ gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (show_dialog), (gpointer) dialog);
+
+ gtk_container_add (GTK_CONTAINER(fBox[fTop]), button);
+ gtk_container_add (GTK_CONTAINER(dialog), box);
+ gtk_widget_show (button);
+ gtk_widget_show(box);
+ pushBox(kBoxMode, box);
+}
+
+
+
+
+// --------------------------- Check Button ---------------------------
+
+struct uiCheckButton : public uiItem
+{
+ GtkToggleButton* fButton;
+
+ uiCheckButton(UI* ui, float* zone, GtkToggleButton* b) : uiItem(ui, zone), fButton(b) {}
+
+ static void toggled (GtkWidget *widget, gpointer data)
+ {
+ float v = (GTK_TOGGLE_BUTTON (widget)->active) ? 1.0 : 0.0;
+ ((uiItem*)data)->modifyZone(v);
+ }
+
+ virtual void reflectZone()
+ {
+ float v = *fZone;
+ fCache = v;
+ gtk_toggle_button_set_active(fButton, v > 0.0);
+ }
+};
+
+void GTKUI::addCheckButton(const char* label, float* zone)
+{
+ *zone = 0.0;
+ GtkWidget* button = gtk_check_button_new_with_label (label);
+ addWidget(label, button);
+
+ uiCheckButton* c = new uiCheckButton(this, zone, GTK_TOGGLE_BUTTON(button));
+ gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC(uiCheckButton::toggled), (gpointer) c);
+
+ checkForTooltip(zone, button);
+}
+
+
+// --------------------------- Adjustmenty based widgets ---------------------------
+
+struct uiAdjustment : public uiItem
+{
+ GtkAdjustment* fAdj;
+
+ uiAdjustment(UI* ui, float* zone, GtkAdjustment* adj) : uiItem(ui, zone), fAdj(adj) {}
+
+ static void changed (GtkWidget *widget, gpointer data)
+ {
+ float v = GTK_ADJUSTMENT (widget)->value;
+ ((uiItem*)data)->modifyZone(v);
+ }
+
+ virtual void reflectZone()
+ {
+ float v = *fZone;
+ fCache = v;
+ gtk_adjustment_set_value(fAdj, v);
+ }
+};
+
+static int precision(double n)
+{
+ if (n < 0.009999) return 3;
+ else if (n < 0.099999) return 2;
+ else if (n < 0.999999) return 1;
+ else return 0;
+}
+
+// -------------------------- Vertical Slider -----------------------------------
+
+void GTKUI::addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step)
+{
+ *zone = init;
+ GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0);
+
+ uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
+
+ gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
+
+ GtkWidget* slider = gtk_vscale_new (GTK_ADJUSTMENT(adj));
+ gtk_range_set_inverted (GTK_RANGE(slider), TRUE);
+ gtk_scale_set_digits(GTK_SCALE(slider), precision(step));
+ float size = 160 * pow(2, fGuiSize[zone]);
+ gtk_widget_set_usize(slider, -1, size);
+
+ if (label && label[0]!=0) {
+ openFrameBox(label);
+ addWidget(label, slider);
+ closeBox();
+ } else {
+ addWidget(label, slider);
+ }
+
+ checkForTooltip(zone, slider);
+}
+
+// -------------------------- Horizontal Slider -----------------------------------
+
+void GTKUI::addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step)
+{
+ *zone = init;
+ GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0);
+
+ uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
+
+ gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
+
+ GtkWidget* slider = gtk_hscale_new (GTK_ADJUSTMENT(adj));
+ gtk_scale_set_digits(GTK_SCALE(slider), precision(step));
+ float size = 160 * pow(2, fGuiSize[zone]);
+ gtk_widget_set_usize(slider, size, -1);
+
+ if (label && label[0]!=0) {
+ openFrameBox(label);
+ addWidget(label, slider);
+ closeBox();
+ } else {
+ addWidget(label, slider);
+ }
+
+ checkForTooltip(zone, slider);
+}
+
+
+// ------------------------------ Num Entry -----------------------------------
+
+void GTKUI::addNumEntry(const char* label, float* zone, float init, float min, float max, float step)
+{
+ *zone = init;
+ GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, step);
+
+ uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
+
+ gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
+
+ GtkWidget* spinner = gtk_spin_button_new (GTK_ADJUSTMENT(adj), 0.005, precision(step));
+
+ //gtk_widget_set_usize(slider, 160, -1);
+ openFrameBox(label);
+ addWidget(label, spinner);
+ closeBox();
+
+ checkForTooltip(zone, spinner);
+}
+
+
+// ========================== passive widgets ===============================
+
+
+// ------------------------------ Progress Bar -----------------------------------
+
+struct uiBargraph : public uiItem
+{
+ GtkProgressBar* fProgressBar;
+ float fMin;
+ float fMax;
+
+ uiBargraph(UI* ui, float* zone, GtkProgressBar* pbar, float lo, float hi)
+ : uiItem(ui, zone), fProgressBar(pbar), fMin(lo), fMax(hi) {}
+
+ float scale(float v) { return (v-fMin)/(fMax-fMin); }
+
+ virtual void reflectZone()
+ {
+ float v = *fZone;
+ fCache = v;
+ gtk_progress_bar_set_fraction(fProgressBar, scale(v));
+ }
+};
+
+
+
+void GTKUI::addVerticalBargraph(const char* label, float* zone, float lo, float hi)
+{
+ GtkWidget* pb = gtk_progress_bar_new();
+ gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pb), GTK_PROGRESS_BOTTOM_TO_TOP);
+ gtk_widget_set_size_request(pb, 8, -1);
+ new uiBargraph(this, zone, GTK_PROGRESS_BAR(pb), lo, hi);
+ openFrameBox(label);
+ addWidget(label, pb);
+ closeBox();
+
+ checkForTooltip(zone, pb);
+}
+
+
+void GTKUI::addHorizontalBargraph(const char* label, float* zone, float lo, float hi)
+{
+ GtkWidget* pb = gtk_progress_bar_new();
+ gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pb), GTK_PROGRESS_LEFT_TO_RIGHT);
+ gtk_widget_set_size_request(pb, -1, 8);
+ new uiBargraph(this, zone, GTK_PROGRESS_BAR(pb), lo, hi);
+ openFrameBox(label);
+ addWidget(label, pb);
+ closeBox();
+
+ checkForTooltip(zone, pb);
+}
+
+
+// ------------------------------ Num Display -----------------------------------
+
+struct uiNumDisplay : public uiItem
+{
+ GtkLabel* fLabel;
+ int fPrecision;
+
+ uiNumDisplay(UI* ui, float* zone, GtkLabel* label, int precision)
+ : uiItem(ui, zone), fLabel(label), fPrecision(precision) {}
+
+ virtual void reflectZone()
+ {
+ float v = *fZone;
+ fCache = v;
+ char s[64];
+ if (fPrecision <= 0) {
+ snprintf(s, 63, "%d", int(v));
+ } else if (fPrecision>3) {
+ snprintf(s, 63, "%f", v);
+ } else {
+ const char* format[] = {"%.1f", "%.2f", "%.3f"};
+ snprintf(s, 63, format[fPrecision-1], v);
+ }
+ gtk_label_set_text(fLabel, s);
+ }
+};
+
+
+void GTKUI::addNumDisplay(const char* label, float* zone, int precision )
+{
+ GtkWidget* lw = gtk_label_new("");
+ new uiNumDisplay(this, zone, GTK_LABEL(lw), precision);
+ openFrameBox(label);
+ addWidget(label, lw);
+ closeBox();
+
+ checkForTooltip(zone, lw);
+}
+
+
+// ------------------------------ Text Display -----------------------------------
+
+struct uiTextDisplay : public uiItem
+{
+ GtkLabel* fLabel;
+ const char** fNames;
+ float fMin;
+ float fMax;
+ int fNum;
+
+
+ uiTextDisplay (UI* ui, float* zone, GtkLabel* label, const char* names[], float lo, float hi)
+ : uiItem(ui, zone), fLabel(label), fNames(names), fMin(lo), fMax(hi)
+ {
+ fNum = 0;
+ while (fNames[fNum] != 0) fNum++;
+ }
+
+ virtual void reflectZone()
+ {
+ float v = *fZone;
+ fCache = v;
+
+ int idx = int(fNum*(v-fMin)/(fMax-fMin));
+
+ if (idx < 0) idx = 0;
+ else if (idx >= fNum) idx = fNum-1;
+
+ gtk_label_set_text(fLabel, fNames[idx]);
+ }
+};
+
+
+void GTKUI::addTextDisplay(const char* label, float* zone, const char* names[], float lo, float hi )
+{
+ GtkWidget* lw = gtk_label_new("");
+ new uiTextDisplay (this, zone, GTK_LABEL(lw), names, lo, hi);
+ openFrameBox(label);
+ addWidget(label, lw);
+ closeBox();
+
+ checkForTooltip(zone, lw);
+}
+
+
+
+void GTKUI::show()
+{
+ assert(fTop == 0);
+ gtk_widget_show (fBox[0]);
+ gtk_widget_show (fWindow);
+}
+
+
+/**
+ * Update all user items reflecting zone z
+ */
+
+static gboolean callUpdateAllGuis(gpointer)
+{
+ UI::updateAllGuis();
+ return TRUE;
+}
+
+
+void GTKUI::run()
+{
+ assert(fTop == 0);
+ gtk_widget_show (fBox[0]);
+ gtk_widget_show (fWindow);
+ gtk_timeout_add(40, callUpdateAllGuis, 0);
+ gtk_main ();
+ stop();
+}
+
+
+
+
+/******************************************************************************
+*******************************************************************************
+
+ DSP
+
+*******************************************************************************
+*******************************************************************************/
+
+
+//---------------------------------------------------
+// tableaux de buffers initialis� par allocChannels
+//---------------------------------------------------
+
+float* gInChannel[256];
+float* gOutChannel[256];
+
+void allocChannels (int size, int numInChan, int numOutChan)
+{
+
+ assert (numInChan < 256);
+ assert (numOutChan < 256);
+
+
+ for (int i = 0; i < numInChan; i++) {
+ gInChannel[i] = (float*) calloc (size, sizeof(float));
+ for (int j = 0; j < size; j++) {
+ gInChannel[i][j] = 0.0;
+ }
+ }
+
+ for (int i = 0; i < numOutChan; i++) {
+ gOutChannel[i] = (float*) calloc (size, sizeof(float));
+ for (int j = 0; j < size; j++) {
+ gOutChannel[i][j] = 0.0;
+ }
+ }
+}
+
+
+
+//----------------------------------------------------------------
+// d�inition du processeur de signal
+//----------------------------------------------------------------
+
+class dsp {
+ protected:
+ int fSamplingFreq;
+ public:
+ dsp() {}
+ virtual ~dsp() {}
+
+ virtual int getNumInputs() = 0;
+ virtual int getNumOutputs() = 0;
+ virtual void buildUserInterface(UI* interface) = 0;
+ virtual void init(int samplingRate) = 0;
+ virtual void compute(int len, float** inputs, float** outputs) = 0;
+};
+
+
+
+/********************END ARCHITECTURE SECTION (part 1/2)****************/
+
+/**************************BEGIN USER SECTION **************************/
+
+<<includeclass>>
+
+/***************************END USER SECTION ***************************/
+
+/*******************BEGIN ARCHITECTURE SECTION (part 2/2)***************/
+
+
+mydsp DSP;
+
+
+
+
+/******************************************************************************
+*******************************************************************************
+
+ MAIN PLAY THREAD
+
+*******************************************************************************
+*******************************************************************************/
+
+// Scan Command Line Arguments
+
+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;
+}
+
+
+//-------------------------------------------------------------------------
+// MAIN
+//-------------------------------------------------------------------------
+pthread_t guithread;
+
+void* run_ui(void* ptr)
+{
+ UI* interface = (UI*) ptr;
+ interface->run();
+ pthread_exit(0);
+ return 0;
+}
+
+int main(int argc, char *argv[] )
+{
+ UI* interface = new GTKUI(argv[0], &argc, &argv);
+ // compute rcfilename to (re)store application state
+ char rcfilename[256];
+ char* home = getenv("HOME");
+ snprintf(rcfilename, 255, "%s/.%src", home, basename(argv[0]));
+
+ AudioInterface audio (
+ AudioParam().frequency(lopt(argv, "--frequency", 44100))
+ .buffering(lopt(argv, "--buffer", 128))
+ //.mode( ((DSP.getNumInputs()>0)?kRead:0) | ((DSP.getNumOutputs()>0)?kWrite:0) )
+ );
+ audio.open();
+ audio.info();
+
+ DSP.init(audio.getSamplingFrequency());
+ DSP.buildUserInterface(interface);
+
+ interface->recallState(rcfilename);
+
+ pthread_create(&guithread, NULL, run_ui, interface);
+
+ float* inChannel[256];
+ float* outChannel[256];
+ int fpb = audio.getFramesPerBuffer();
+
+ audio.allocChanGroup(inChannel, max(audio.getNumInputs(), DSP.getNumInputs()), fpb);
+ audio.allocChanGroup(outChannel, max(audio.getNumOutputs(), DSP.getNumOutputs()), fpb);
+ setRealtimePriority();
+ AVOIDDENORMALS;
+ // Sound processing loop
+ audio.write(fpb, outChannel);
+ audio.write(fpb, outChannel);
+ while(!interface->stopped()) {
+ if ( !audio.write(fpb, outChannel)) printf("w");
+ if ( !audio.read (fpb, inChannel)) printf("r");;
+ STARTMESURE
+ DSP.compute(fpb, inChannel, outChannel);
+ STOPMESURE
+ }
+
+ audio.close();
+ interface->saveState(rcfilename);
+
+#ifdef BENCHMARKMODE
+ printstats();
+#endif
+ //wait(0);
+ return 0;
+}
+
+/********************END ARCHITECTURE SECTION (part 2/2)****************/
+
+