Logo Search packages:      
Sourcecode: faust version File versions  Download package

alchemy-as.cpp

// Faust -> Alchemy -> ActionScript C++ Architecture File

#include "AS3.h"
// math.h is needed for many faust examples, so include it.
// otherwise we have to hand-edit c++.
#include "math.h" 


#ifdef __GNUC__

#define max(x,y) (((x)>(y)) ? (x) : (y))
#define min(x,y) (((x)<(y)) ? (x) : (y))

// abs is now predefined
//template<typename T> T abs (T a)              { return (a<T(0)) ? -a : a; }

inline int        lsr (int x, int n)                  { return int(((unsigned int)x) >> n); }
#else 
#endif
/******************************************************************************
*******************************************************************************

                                                 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>>

/******************************************************************************
*******************************************************************************

                  ABSTRACT USER INTERFACE

*******************************************************************************
*******************************************************************************/

00042 class UI
{

public:
      bool fStopped;
      UI() : fStopped(false) {}
      virtual ~UI() {}
      
      virtual void addButton(char* label, float* zone) = 0;
      virtual void addToggleButton(char* label, float* zone) = 0;
      virtual void addCheckButton(char* label, float* zone) = 0;
      virtual void addVerticalSlider(char* label, float* zone, float init, float min, float max, float step) = 0;
      virtual void addHorizontalSlider(char* label, float* zone, float init, float min, float max, float step) = 0;
      virtual void addNumEntry(char* label, float* zone, float init, float min, float max, float step) = 0;
      
      virtual void openFrameBox(char* label) = 0;
      virtual void openTabBox(char* label) = 0;
      virtual void openHorizontalBox(char* label) = 0;
      virtual void openVerticalBox(char* label) = 0;
      virtual void closeBox() = 0;
      
      virtual void run() = 0;
      
      void stop() { fStopped = true; }
      bool stopped()    { return fStopped; }
};


////// Implementation of UI

// Faust UI hookup is straightforward
// We subclass from UI and then these methods will get called by compiled
// Faust which assembles controls. We handle all UI as needed, and when
// things changed we need to set *zone=(some value).
// This is a little more complicated when jumping between alchemy/script.
// So rather than deal with marshalling pointers, just cheat and use an int:pointer map.
// NOTE: I should have used an STL map but that wasn't working back when I used gcc.
// I'll investigate since that would clean up the code quite a bit.

// upper bound on number of controls
#define MAX_CONTROLS 25
// map of unique ui control id to a float* where faust reads the corresponding value.
static float* uidToZone[MAX_CONTROLS];
// Counter that assigns control IDs.
static int uiMap_id = 0;

// I wasn't able to properly thunk the UI actionscrpit methods to C.
// Since we know the complete UI at the time of creation, a collection of
// ui creation info is passed back from faust_init, and actionscript
// can read it and create the UI.
// This is a little messy and was done last-minute.
const int TYPE_BUTTON = 0;
const int TYPE_TOGGLE = 1;
const int TYPE_SLIDER = 2;
// max length for a  label - more than 50 chars will get cut.
#define LABEL_LEN 50
struct uiElemInfo {
  int type;
  int id;
  char label[LABEL_LEN+1];
  float min;
  float max;
  float init;
  float step;
};
uiElemInfo uielems[MAX_CONTROLS];
static int uielems_size = 0;

// todo: stdio.h has strncpy, I just got paranoid about extra includes making the code bigger :)
void strcopy(char *src, char *dst) {
  dst[LABEL_LEN] = '\0';
  for (int i = 0; i < LABEL_LEN; ++i) {
    if (0 == (dst[i] = src[i])) return;
  }
}

void BuildUIArray(AS3_Val &array) {
  for (int i = 0; i < uielems_size; ++i) {
    AS3_Val result = AS3_Object( "type:AS3ValType,id:AS3ValType,label:AS3ValType,min:AS3ValType,max:AS3ValType,init:AS3ValType,step:AS3ValType",
            AS3_Int(uielems[i].type),
            AS3_Int(uielems[i].id),
            AS3_String(uielems[i].label),
            AS3_Number(uielems[i].min),
            AS3_Number(uielems[i].max),
            AS3_Number(uielems[i].init),
            AS3_Number(uielems[i].step)
      );
      AS3_Set(array, AS3_Int(i), result);
      // decrease refcount? todo: this may leak memory...
      //AS3_Release(result);
  }
}

class ActionScriptUI : public UI {
 public:
  ActionScriptUI() {
    fStopped = false;
  }
  
  virtual ~ActionScriptUI() {
  }
  
  // Pass in a zone, get back a unique ID.
  int registerControl(float *zone) {
    if (!zone) return 0;
      uiMap_id++;
      uidToZone[uiMap_id] = zone;
      return uiMap_id;
  }
  
  // Called from Flash when any control is updated.
  // Results will take effect on the next dsp callback
  // since everything runs in the same thread.
  void updateControl(int id, float value) {
    *(uidToZone[id]) = value;
  }


      virtual void addButton(char* label, float* zone) {
        int id = registerControl(zone);
        uielems[uielems_size].type = TYPE_BUTTON;
        uielems[uielems_size].id = id;
        strcopy(label, uielems[uielems_size].label);
        uielems[uielems_size].min = 0;
        uielems[uielems_size].max = 0;
        uielems[uielems_size].init = 0;
        uielems[uielems_size].step = 0;
      uielems_size++;
      }
      virtual void addToggleButton(char* label, float* zone) {
        int id = registerControl(zone);
        uielems[uielems_size].type = TYPE_TOGGLE;
        uielems[uielems_size].id = id;
        strcopy(label, uielems[uielems_size].label);
        uielems[uielems_size].min = 0;
        uielems[uielems_size].max = 0;
        uielems[uielems_size].init = 0;
        uielems[uielems_size].step = 0;
      uielems_size++;
      }
      virtual void addCheckButton(char* label, float* zone) {
        return addToggleButton(label, zone);
      }
      virtual void addVerticalSlider(char* label, float* zone, float init, float min, float max, float step) {
        return addHorizontalSlider(label, zone, init, min, max, step);
      }
      virtual void addHorizontalSlider(char* label, float* zone, float init, float min, float max, float step) {
        int id = registerControl(zone);
        uielems[uielems_size].type = TYPE_SLIDER;
        uielems[uielems_size].id = id;
        strcopy(label, uielems[uielems_size].label);
        uielems[uielems_size].min = min;
        uielems[uielems_size].max = max;
        uielems[uielems_size].init = init;
        uielems[uielems_size].step = step;
      uielems_size++;
      }
      virtual void addNumEntry(char* label, float* zone, float init, float min, float max, float step) {
        return addHorizontalSlider(label, zone, init, min, max, step);
      }
            
      // Not implemented yet - these only affect UI layout and aren't critical.
      // See actionscript comments for details.
      virtual void openFrameBox(char* label) {}
      virtual void openTabBox(char* label) {}
      virtual void openHorizontalBox(char* label) {}
      virtual void openVerticalBox(char* label) {}
      virtual void closeBox() {}
      virtual void run() { }
};

/******************************************************************************
*******************************************************************************

                      FAUST DSP

*******************************************************************************
*******************************************************************************/



//----------------------------------------------------------------
//  abstract definition of a signal processor
//----------------------------------------------------------------
                  
00227 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;
};
            

//----------------------------------------------------------------------------
//  FAUST generated signal processor
//----------------------------------------------------------------------------
            

<<includeclass>>




/// Alchemy DSP
00253 class Faust {
public:
  Faust() { 
    // mydsp will be defined by faust in 'includeclass'
    dsp_ = new mydsp();
      ui_ = new ActionScriptUI();
      dsp_->buildUserInterface(ui_);
    dsp_->init(44100); // 44.1k, 2 channels, @ 32-bit is hardcoded into flash player 10.
  }
  
  ~Faust() {
    if (dsp_) delete dsp_;
      if (ui_) delete ui_;
  }
  
//private:
public: // we're all friends here
      mydsp *dsp_;
      ActionScriptUI *ui_;
};


Faust *faust = NULL;

// Alchemy wrapper interface code
static AS3_Val api_init(void *thisPtr, AS3_Val args) {
  faust = new Faust();
  AS3_Val array = AS3_Array("");
  BuildUIArray(array);
  return array;
}

static AS3_Val api_shutdown(void *thisPtr, AS3_Val args) {
      if (faust)
            delete faust;
      return AS3_Int(0);
}

// args = int id, float value
static AS3_Val api_onControlChange(void *thisPtr, AS3_Val args) {
  if (!faust) return AS3_Int(0);

  // Marshall the arguments in.
  int id = 0;
  AS3_Val controlVal;
  AS3_ArrayValue(args, "IntType, AS3ValType", &id, &controlVal);
  double control_double = AS3_NumberValue(controlVal);

  // loss of precision is ok.
  float value = (float)control_double;

  // Call the actual update function
  faust->ui_->updateControl(id, value);
  return AS3_Int(0);
}

#define MAX_FLASH_BUFFER 8192
// output buffers - L/R channels separate
static float bufferL[MAX_FLASH_BUFFER];
static float bufferR[MAX_FLASH_BUFFER];
// output buffer - construct interleaved output
static float bufferSum[2*MAX_FLASH_BUFFER];

// input buffers - L/R separate
static float inputL[MAX_FLASH_BUFFER];
static float inputR[MAX_FLASH_BUFFER];
// input buffer scratch space - interleaved
static float bufferInSum[2*MAX_FLASH_BUFFER];

// This is the most 'interesting' function of the file - it takes in flash sound buffers
//  and sends them through Faust DSP code.
// args = int nsamples, float* buffer (byte[] in flash)
static AS3_Val api_tick(void *thisPtr, AS3_Val args) {
  if (!faust) return AS3_Int(0);

  // Marshall arguments in.
  int nsamples = 0;
  int use_input = 0;
  AS3_Val buffer;
  AS3_Val input;
  AS3_ArrayValue(args, "IntType, IntType, AS3ValType, AS3ValType", &nsamples, &use_input, &input, &buffer);
  
  float* outputs[2] = {bufferL, bufferR};
  float* inputs[2] = {inputL, inputR};
  if (use_input) {
    //AS3_ByteArray_seek(input, 0, 0);
      // we need (#samples * sizeof(float) * 2 channels) bytes.
    AS3_ByteArray_readBytes((char*)bufferInSum, input, nsamples * 4 * 2);
      char *src = (char*)bufferInSum;
      char *dl = (char*)inputL, *dr = (char*)inputR;
      for (int i = 0; i < nsamples; ++i) {
        // fix endianness
        dl[3] = src[0];
        dl[2] = src[1];
        dl[1] = src[2];
        dl[0] = src[3];
        dr[3] = src[4];
        dr[2] = src[5];
        dr[1] = src[6];
        dr[0] = src[7];
        dl += 4;
        dr += 4;
        src += 8;
      }
  }

  // magic!
  faust->dsp_->compute(nsamples, inputs, outputs);

  
  // Post-process: interleave arrays. 
  // Faust outputs to two separate arrays (which are probably contiguous in memory - see above)
  // Flash's sound callback needs this as LRLRLRLR...
  // For added fun, LLVM internal float seems to be the opposite endianness
  // as what Flash uses, so we have to do this byte-by-byte.
  char *copyL = (char*)bufferL;
  char *copyR = (char*)bufferR;
  char *tape_head = (char*)bufferSum;
  for (int i = 0; i < nsamples; ++i) {
    *tape_head++ = copyL[3];
      *tape_head++ = copyL[2];
      *tape_head++ = copyL[1];
      *tape_head++ = copyL[0];
      *tape_head++ = copyR[3];
      *tape_head++ = copyR[2];
      *tape_head++ = copyR[1];
      *tape_head++ = copyR[0];
      copyL+=4;
      copyR+=4;
  }
  AS3_ByteArray_writeBytes(buffer, bufferSum, 4 * nsamples * 2);

  return AS3_Int(0);
}


//Alchemy entry point
// Here we are responsible for contructing an API object to pass back to Flash.
// This must contain pointers to all functions which may be called.
int main()
{
      //define the methods exposed to ActionScript
      //typed as an ActionScript Function instance
      AS3_Val methodInit = AS3_Function( NULL, api_init );
      AS3_Val methodShutdown = AS3_Function( NULL, api_shutdown );
      AS3_Val methodOnControlChange = AS3_Function( NULL, api_onControlChange );
      AS3_Val methodTick = AS3_Function( NULL, api_tick );

      // construct an API lookup table with references to all functions
      // In flash we'll instantiate one of these and call methods on it
      // e.g. faust.api_tick().
      AS3_Val result = AS3_Object(
            "api_init:AS3ValType, api_shutdown:AS3ValType, api_onControlChange:AS3ValType, api_tick:AS3ValType",
            methodInit, methodShutdown, methodOnControlChange, methodTick);

      AS3_Release(methodInit);
      AS3_Release(methodShutdown);
      AS3_Release(methodOnControlChange);
      AS3_Release(methodTick);

      // notify Flash of our functions and run -- this function never returns.
      AS3_LibInit(result);

      return 0;
}


Generated by  Doxygen 1.6.0   Back to index