Files
FrameLib/FrameLib_Framework/FrameLib_DSP.h
T

395 lines
13 KiB
C++

#ifndef FRAMELIB_DSP_H
#define FRAMELIB_DSP_H
#include "FrameLib_Types.h"
#include "FrameLib_Context.h"
#include "FrameLib_Object.h"
#include "FrameLib_ProcessingQueue.h"
#include <algorithm>
#include <limits>
#include <memory>
#include <vector>
// FrameLib_DSP
// This abstract class is the core of the DSP processing system and handles low level single channel connections and timing
class FrameLib_DSP : public FrameLib_Block, public FrameLib_Queueable<FrameLib_DSP>
{
// Typedefs for concision / Queue access
typedef FrameLib_Queueable<FrameLib_Block>::Queue Queue;
typedef FrameLib_Queueable<FrameLib_DSP>::Queue LocalQueue;
typedef FrameLib_Parameters::Serial Serial;
friend class FrameLib_ProcessingQueue;
protected:
struct SchedulerInfo
{
SchedulerInfo()
: mTimeAdvance(0), mNewFrame(false), mOutputDone(false) {}
SchedulerInfo(FrameLib_TimeFormat timeAdvance, bool newFrame, bool outputDone)
: mTimeAdvance(timeAdvance), mNewFrame(newFrame), mOutputDone(outputDone) {}
FrameLib_TimeFormat mTimeAdvance;
bool mNewFrame;
bool mOutputDone;
};
private:
struct Input
{
Input() : mObject(nullptr), mIndex(0), mSize(0), mFixedInput(nullptr), mType(kFrameNormal), mUpdate(false), mParameters(false), mTrigger(true), mSwitchable(false) {}
FrameType getCurrentType() const { return mObject ? mObject->mOutputs[mIndex].mCurrentType : kFrameNormal; }
// Connection Info
FrameLib_DSP *mObject;
unsigned long mIndex;
// Fixed Input
unsigned long mSize;
double *mFixedInput;
// Flags
FrameType mType;
bool mUpdate;
bool mParameters;
bool mTrigger;
bool mSwitchable;
};
struct Output
{
Output() : mMemory(nullptr), mType(kFrameNormal), mCurrentType(kFrameNormal), mRequestedType(kFrameNormal), mCurrentSize(0), mRequestedSize(0), mPointerOffset(0) {}
void *mMemory;
FrameType mType;
FrameType mCurrentType;
FrameType mRequestedType;
size_t mCurrentSize;
size_t mRequestedSize;
size_t mPointerOffset;
};
public:
// Constructor / Destructor
FrameLib_DSP(ObjectType type, FrameLib_Context context, FrameLib_Proxy *proxy, FrameLib_Parameters::Info *info, unsigned long nIns, unsigned long nOuts, unsigned long nAudioChans = 0);
~FrameLib_DSP();
// Set Fixed Inputs
void setFixedInput(unsigned long idx, double *input, unsigned long size) final;
const double *getFixedInput(unsigned long idx, unsigned long *size) final;
// Audio Processing
void blockUpdate(const double * const *ins, double **outs, unsigned long blockSize) final;
void reset(double samplingRate, unsigned long maxBlockSize) final;
// Info (individual objects should override other methods to provide info)
const FrameLib_Parameters *getParameters() const final { return &mParameters; }
FrameType inputType(unsigned long idx) const final { return mInputs[idx].mType; }
FrameType outputType(unsigned long idx) const final { return mOutputs[idx].mType; }
// Automatic Ordering Connections
void autoOrderingConnections() final;
void clearAutoOrderingConnections() final;
protected:
// Setup and IO Modes
// Call these from your constructor only (unsafe elsewhere)
void setIO(unsigned long nIns, unsigned long nOuts, unsigned long nAudioChans = 0);
void setInputMode(unsigned long idx, bool update, bool trigger, bool switchable, FrameType type = kFrameNormal);
void setParameterInput(unsigned long idx);
void addParameterInput();
void setOutputType(unsigned long idx, FrameType type);
// You should only call this from your process method (it is unsafe anywhere else)
void setCurrentOutputType(unsigned long idx, FrameType type);
// You should only call this from your update method (it is unsafe anywhere else)
void updateTrigger(unsigned long idx, bool trigger);
// Processing Utilities
// Test if an Input Triggered the Current Frame
bool isTrigger(unsigned long idx) const { return mInputs[idx].mTrigger && mInputs[idx].mObject && (mInputs[idx].mObject->mFrameTime == mFrameTime); }
// Timing
FrameLib_TimeFormat getFrameTime() const { return mFrameTime; }
FrameLib_TimeFormat getValidTime() const { return mValidTime; }
FrameLib_TimeFormat getInputTime() const { return mInputTime; }
FrameLib_TimeFormat getCurrentTime() const { return getType() == kScheduler ? mValidTime : mFrameTime; }
FrameLib_TimeFormat getBlockStartTime() const { return getType() == kOutput ? mBlockEndTime : mBlockStartTime; }
FrameLib_TimeFormat getBlockEndTime() const { return mBlockEndTime; }
FrameLib_TimeFormat getInputFrameTime(unsigned long idx) const { return mInputs[idx].mObject ? mInputs[idx].mObject->mFrameTime : FrameLib_TimeFormat(0); }
FrameLib_TimeFormat getInputValidTime(unsigned long idx) const { return mInputs[idx].mObject ? mInputs[idx].mObject->mValidTime : FrameLib_TimeFormat(0); }
// Output Allocation
void requestOutputSize(unsigned long idx, size_t size) { mOutputs[idx].mRequestedSize = size; }
void requestAddedOutputSize(unsigned long idx, size_t size) { mOutputs[idx].mRequestedSize += size; }
bool allocateOutputs();
// Get Inputs and Outputs
FrameType getInputCurrentType(unsigned long idx) const { return mInputs[idx].getCurrentType(); }
const double *getInput(unsigned long idx, size_t *size) const;
const FrameLib_Parameters::Serial *getInput(unsigned long idx) const;
FrameType getOutputCurrentType(unsigned long idx) const { return mOutputs[idx].mCurrentType; }
double *getOutput(unsigned long idx, size_t *size) const;
FrameLib_Parameters::Serial *getOutput(unsigned long idx) const;
// Convience methods for copying and zeroing
void prepareCopyInputToOutput(unsigned long inIdx, unsigned long outIdx);
void copyInputToOutput(unsigned long inIdx, unsigned long outIdx);
static void copyVector(double *output, const double *input, unsigned long size) { std::copy(input, input + size, output); }
static void zeroVector(double *output, unsigned long size) { std::fill_n(output, size, 0.0); }
static void copyVectorExtend(double* output, const double *input, unsigned long sizeOut, unsigned long sizeIn)
{
copyVector(output, input, std::min(sizeIn, sizeOut));
std::fill_n(output + sizeIn, (sizeOut > sizeIn) ? sizeOut - sizeIn : 0, input[sizeIn - 1]);
}
static void copyVectorWrap(double* output, const double *input, unsigned long sizeOut, unsigned long sizeIn)
{
unsigned long excess = sizeOut % sizeIn;
for (unsigned long i = 0; i < (sizeOut - excess); i += sizeIn)
copyVector(output + i, input, sizeIn);
copyVector(output + (sizeOut - excess), input, excess);
}
static void copyVectorZero(double* output, const double *input, unsigned long sizeOut, unsigned long sizeIn)
{
copyVector(output, input, std::min(sizeIn, sizeOut));
zeroVector(output + sizeIn, (sizeOut > sizeIn) ? sizeOut - sizeIn : 0);
}
private:
// Deleted
FrameLib_DSP(const FrameLib_DSP&) = delete;
FrameLib_DSP& operator=(const FrameLib_DSP&) = delete;
// Queueable Reset
void reset(LocalQueue *queue);
// Customisable Processing
// Override to handle audio at the block level
virtual void blockProcess(const double * const *ins, double **outs, unsigned long blockSize) {}
// Override to get called on audio reset
virtual void objectReset() {}
// Override for updates prior to schedule / process (e.g. adjusting triggers)
virtual void update() {}
// Override for scheduling code (scheduler objects must override this)
virtual SchedulerInfo schedule(bool newFrame, bool noAdvance) = 0;
// Override for main frame processing code (processor objects must override this)
virtual void process() = 0;
// Scheduling
// This returns true if the object requires notification from an audio thread (is a scheduler/has audio input)
bool requiresAudioNotification() { return getType() == kScheduler || getNumAudioIns(); }
// Manage Output Memory
inline void freeOutputMemory();
inline void releaseOutputMemory();
// Dependency Notification
inline void dependencyNotify(bool releaseMemory, bool fromInput);
void dependenciesReady();
void setOutputDependencyCount();
void incrementInputDependency();
// Connections
void connectionUpdate(Queue *queue) final;
void autoOrderingConnections(LocalQueue *queue);
protected:
// Member Variables
// Sampling Rate and Maximum Block Size
double mSamplingRate;
unsigned long mMaxBlockSize;
// Parameters
FrameLib_Parameters mParameters;
private:
// Processing Queue
FrameLib_Context::ProcessingQueue mProcessingQueue;
FrameLib_DSP *mNext;
// IO Info
std::vector<FrameLib_DSP *> mInputDependencies;
std::vector<FrameLib_DSP *> mOutputDependencies;
std::vector <Input> mInputs;
std::vector <Output> mOutputs;
// Dependency Counts
long mInputCount;
long mDependencyCount;
long mOutputMemoryCount;
// Frame and Block Timings
FrameLib_TimeFormat mFrameTime;
FrameLib_TimeFormat mValidTime;
FrameLib_TimeFormat mInputTime;
FrameLib_TimeFormat mBlockStartTime;
FrameLib_TimeFormat mBlockEndTime;
bool mUpdatingInputs;
bool mNoLiveInputs;
bool mInUpdate;
bool mOutputDone;
};
// ************************************************************************************** //
// FrameLib_Processor - Simple class for process type objects (can't handle audio)
class FrameLib_Processor : public FrameLib_DSP
{
public:
FrameLib_Processor(FrameLib_Context context, FrameLib_Proxy *proxy, FrameLib_Parameters::Info *info, unsigned long nIns = 0, unsigned long nOuts = 0)
: FrameLib_DSP(kProcessor, context, proxy, info, nIns, nOuts) {}
static ObjectType getType() { return kProcessor; }
static bool handlesAudio() { return false; }
protected:
// This prevents the user from needing to implement this method - doing so will do nothing
virtual SchedulerInfo schedule(bool newFrame, bool noAdvance) { return SchedulerInfo(); }
void setIO(unsigned long nIns, unsigned long nOuts) { FrameLib_DSP::setIO(nIns, nOuts); }
};
// ************************************************************************************** //
// FrameLib_AudioInput - Simple class for process type objects (can handle audio input)
class FrameLib_AudioInput : public FrameLib_DSP
{
public:
FrameLib_AudioInput(FrameLib_Context context, FrameLib_Proxy *proxy, FrameLib_Parameters::Info *info, unsigned long nIns = 0, unsigned long nOuts = 0, unsigned long nAudioIns = 0)
: FrameLib_DSP(kProcessor, context, proxy, info, nIns, nOuts, nAudioIns) {}
static ObjectType getType() { return kProcessor; }
static bool handlesAudio() { return true; }
protected:
// This prevents the user from needing to implement this method - doing so will do nothing
virtual SchedulerInfo schedule(bool newFrame, bool noAdvance) { return SchedulerInfo(); }
};
// ************************************************************************************** //
// FrameLib_AudioOutput - Simple class for process type objects (can handle audio output)
class FrameLib_AudioOutput : public FrameLib_DSP
{
public:
FrameLib_AudioOutput(FrameLib_Context context, FrameLib_Proxy *proxy, FrameLib_Parameters::Info *info, unsigned long nIns = 0, unsigned long nOuts = 0, unsigned long nAudioOuts = 0)
: FrameLib_DSP(kOutput, context, proxy, info, nIns, nOuts, nAudioOuts) {}
static ObjectType getType() { return kOutput; }
static bool handlesAudio() { return true; }
protected:
// This prevents the user from needing to implement this method - doing so will do nothing
virtual SchedulerInfo schedule(bool newFrame, bool noAdvance) { return SchedulerInfo(); }
};
// ************************************************************************************** //
// FrameLib_Scheduler - Simple class for scheduler type objects
class FrameLib_Scheduler : public FrameLib_DSP
{
public:
FrameLib_Scheduler(FrameLib_Context context, FrameLib_Proxy *proxy, FrameLib_Parameters::Info *info, unsigned long nIns = 0, unsigned long nOuts = 0, unsigned long nAudioIns = 0)
: FrameLib_DSP(kScheduler, context, proxy, info, nIns, nOuts, nAudioIns) {}
static ObjectType getType() { return kScheduler; }
static bool handlesAudio() { return true; }
protected:
// This prevents the user from needing to implement this method - doing so will do nothing
virtual void process() {}
};
#endif