prepare for patchbay effects

This commit is contained in:
tildearrow 2023-05-21 04:39:36 -05:00
parent 85ef486949
commit 2da1fe8821
9 changed files with 528 additions and 5 deletions

View File

@ -556,6 +556,9 @@ src/engine/platform/dummy.cpp
src/engine/export/abstract.cpp
src/engine/export/amigaValidation.cpp
src/engine/effect/abstract.cpp
src/engine/effect/dummy.cpp
)
if (USE_SNDFILE)

View File

@ -450,7 +450,7 @@ size | description
# patchbay
Furnace dev135 adds a "patchbay" which allows for arbitrary connection of chip outputs to system outputs.
it eventually will allow connecting outputs to effects and so on.
it also allows connecting outputs to effects and so on.
a connection is represented as an unsigned int in the following format:
@ -565,11 +565,11 @@ size | description
| - must be between 32 and 4092.
| - the other slots are reserved for chip/system portsets.
2 | effect ID
| - 0x00: dummy
| - 0x01: external (plugin bridge)
| - 0x01: dummy
| - 0x02: external (plugin bridge)
| - not implemented yet
| - 0x02: volume
| - 0x03: filter (circuit)
| - 0x03: volume
| - 0x04: filter (circuit)
4f | dry/wet balance
2 | reserved
2 | storage version

189
src/engine/effect.h Normal file
View File

@ -0,0 +1,189 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _EFFECT_H
#define _EFFECT_H
#include <stdlib.h>
#include "../ta-utils.h"
class DivEngine;
class DivEffect {
protected:
DivEngine* parent;
public:
/**
* fill a buffer with sound data.
* @param in pointers to input buffers.
* @param out pointers to output buffers.
* @param len the amount of samples in input and output.
*/
virtual void acquire(float** in, float** out, size_t len);
/**
* reset the state of this effect.
*/
virtual void reset();
/**
* get the number of inputs this effect requests.
* @return number of inputs. SHALL NOT be less than zero.
*/
virtual int getInputCount();
/**
* get the number of outputs this effect provides.
* @return number of outputs. SHALL NOT be less than one.
*/
virtual int getOutputCount();
/**
* called when the sample rate changes.
* @param rate the new sample rate.
*/
virtual void rateChanged(double rate);
/**
* get the value of a parameter.
* @param param parameter ID.
* @return a String with the value.
* @throws std::out_of_range if the parameter ID is out of range.
*/
virtual String getParam(size_t param);
/**
* set the value of a parameter.
* @param param parameter ID.
* @param value the value.
* @return whether the parameter was set successfully.
*/
virtual bool setParam(size_t param, String value);
/**
* get a list of parameters.
* @return a C string with a list of parameters, or NULL if there are none.
* PARAMETER TYPES
*
* Parameter
* id:type:name:description:[...]
* type may be one of the following:
* - s: string
* - i: integer
* - I: integer slider
* - r: integer list (radio button)
* - R: integer list (combo box)
* - h: integer hex
* - f: float
* - F: float slider
* - d: double
* - D: double slider
* - b: boolean (checkbox)
* - t: boolean (toggle button)
* - x: X/Y integer
* - X: X/Y float
* - c: color (RGB)
* - C: color (RGBA)
* - B: button
*
* SameLine
* !s
*
* Separator
* ---
*
* Indent/Unindent
* > Indent
* < Unindent
*
* TreeNode
* >> Begin
* << End
*
* Tabs
* >TABBAR BeginTabBar
* >TAB:name Begin
* <TAB End
* <TABBAR EndTabBar
*
* Text
* TEXT:text (static text)
* TEXTF:id (dynamic text)
*
* NOTES
*
* use a new line to separate parameters.
* use `\:` if you need a colon.
*/
virtual const char* getParams();
/**
* get the number of parameters.
* @return count.
*/
virtual size_t getParamCount();
/**
* get a dynamic text.
* @param id the text ID.
* @return a String with the text.
* @throws std::out_of_range if the text ID is out of range.
*/
virtual String getDynamicText(size_t id);
/**
* load effect data.
* @param version effect data version. may be zero.
* @param data effect data. may be NULL.
* @param len effect data length. may be zero.
* @return whether loading was successful.
*/
virtual bool load(unsigned short version, const unsigned char* data, size_t len);
/**
* save effect data.
* @param version effect data version.
* @param len effect data length.
* @return a pointer to effect data. this must be de-allocated manually.
* may also return NULL if it can't save.
*/
virtual unsigned char* save(unsigned short* version, size_t* len);
/**
* initialize this DivEffect.
* @param parent the parent DivEngine.
* @param version effect data version. may be zero.
* @param data effect data. may be NULL.
* @param len effect data length. may be zero.
* @return whether initialization was successful.
*/
virtual bool init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len);
/**
* quit the DivEffect.
*/
virtual void quit();
virtual ~DivEffect();
};
// additional notes:
// - we don't have a GUI API yet, but that will be added when I make the plugin bridge.
#endif

View File

@ -0,0 +1,85 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../effect.h"
#include "../../ta-log.h"
#include <stdexcept>
void DivEffect::acquire(float** in, float** out, size_t len) {
}
void DivEffect::reset() {
}
int DivEffect::getInputCount() {
return 0;
}
int DivEffect::getOutputCount() {
return 0;
}
void DivEffect::rateChanged(double rate) {
}
String DivEffect::getParam(size_t param) {
throw std::out_of_range("param");
// unreachable
return "";
}
bool DivEffect::setParam(size_t param, String value) {
return false;
}
const char* DivEffect::getParams() {
return NULL;
}
size_t DivEffect::getParamCount() {
return 0;
}
String DivEffect::getDynamicText(size_t id) {
throw std::out_of_range("param");
// unreachable
return "";
}
bool DivEffect::load(unsigned short version, const unsigned char* data, size_t len) {
return false;
}
unsigned char* DivEffect::save(unsigned short* version, size_t* len) {
*len=0;
*version=0;
return NULL;
}
bool DivEffect::init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len) {
return false;
}
void DivEffect::quit() {
}
DivEffect::~DivEffect() {
}

View File

@ -0,0 +1,60 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "dummy.h"
void DivEffectDummy::acquire(float** in, float** out, size_t len) {
memcpy(out[0],in[0],len*sizeof(float));
}
void DivEffectDummy::reset() {
}
int DivEffectDummy::getInputCount() {
return 1;
}
int DivEffectDummy::getOutputCount() {
return 1;
}
const char* DivEffectDummy::getParams() {
return NULL;
}
size_t DivEffectDummy::getParamCount() {
return 0;
}
bool DivEffectDummy::load(unsigned short version, const unsigned char* data, size_t len) {
return true;
}
unsigned char* DivEffectDummy::save(unsigned short* version, size_t* len) {
*len=0;
*version=0;
return NULL;
}
bool DivEffectDummy::init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len) {
return false;
}
void DivEffectDummy::quit() {
}

34
src/engine/effect/dummy.h Normal file
View File

@ -0,0 +1,34 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../effect.h"
class DivEffectDummy: public DivEffect {
public:
void acquire(float** in, float** out, size_t len);
void reset();
int getInputCount();
int getOutputCount();
const char* getParams();
size_t getParamCount();
bool load(unsigned short version, const unsigned char* data, size_t len);
unsigned char* save(unsigned short* version, size_t* len);
bool init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len);
void quit();
};

View File

@ -0,0 +1,95 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "engine.h"
#include "effect/dummy.h"
void DivEffectContainer::preAcquire(size_t count) {
if (!count) return;
int inCount=effect->getInputCount();
if (inLen<count) {
for (int i=0; i<inCount; i++) {
if (in[i]!=NULL) {
delete[] in[i];
in[i]=new float[count];
}
}
inLen=count;
}
for (int i=0; i<inCount; i++) {
if (in[i]==NULL) {
in[i]=new float[count];
}
}
}
void DivEffectContainer::acquire(size_t count) {
if (!count) return;
int outCount=effect->getOutputCount();
if (outLen<count) {
for (int i=0; i<outCount; i++) {
if (out[i]!=NULL) {
delete[] out[i];
out[i]=new float[count];
}
}
outLen=count;
}
for (int i=0; i<outCount; i++) {
if (out[i]==NULL) {
out[i]=new float[count];
}
}
effect->acquire(in,out,count);
}
bool DivEffectContainer::init(DivEffectType effectType, DivEngine* eng, double rate, unsigned short version, const unsigned char* data, size_t len) {
switch (effectType) {
case DIV_EFFECT_DUMMY:
default:
effect=new DivEffectDummy;
}
return effect->init(eng,rate,version,data,len);
}
void DivEffectContainer::quit() {
effect->quit();
delete effect;
effect=NULL;
for (int i=0; i<DIV_MAX_OUTPUTS; i++) {
if (in[i]) {
delete[] in[i];
in[i]=NULL;
}
if (out[i]) {
delete[] out[i];
out[i]=NULL;
}
}
inLen=0;
outLen=0;
}

View File

@ -23,6 +23,7 @@
#include "instrument.h"
#include "song.h"
#include "dispatch.h"
#include "effect.h"
#include "export.h"
#include "dataErrors.h"
#include "safeWriter.h"
@ -222,6 +223,25 @@ struct DivDispatchContainer {
}
};
struct DivEffectContainer {
DivEffect* effect;
float* in[DIV_MAX_OUTPUTS];
float* out[DIV_MAX_OUTPUTS];
size_t inLen, outLen;
void preAcquire(size_t count);
void acquire(size_t count);
bool init(DivEffectType effectType, DivEngine* eng, double rate, unsigned short version, const unsigned char* data, size_t len);
void quit();
DivEffectContainer():
effect(NULL),
inLen(0),
outLen(0) {
memset(in,0,DIV_MAX_OUTPUTS*sizeof(float*));
memset(out,0,DIV_MAX_OUTPUTS*sizeof(float*));
}
};
typedef int EffectValConversion(unsigned char,unsigned char);
struct EffectHandler {
@ -411,6 +431,7 @@ class DivEngine {
std::vector<String> midiOuts;
std::vector<DivCommand> cmdStream;
std::vector<DivInstrumentType> possibleInsTypes;
std::vector<DivEffectContainer> effectInst;
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
@ -441,6 +462,7 @@ class DivEngine {
short tremTable[128];
int reversePitchTable[4096];
int pitchTable[4096];
short effectSlotMap[4096];
char c163NameCS[1024];
int midiBaseChan;
bool midiPoly;
@ -514,6 +536,9 @@ class DivEngine {
void swapChannels(int src, int dest);
void stompChannel(int ch);
// recalculate patchbay (UNSAFE)
void recalcPatchbay();
// change song (UNSAFE)
void changeSong(size_t songIndex);
@ -1049,6 +1074,12 @@ class DivEngine {
// move system
bool swapSystem(int src, int dest, bool preserveOrder=true);
// add effect
bool addEffect(DivEffectType which);
// remove effect
bool removeEffect(int index);
// write to register on system
void poke(int sys, unsigned int addr, unsigned short val);
@ -1231,6 +1262,7 @@ class DivEngine {
memset(tremTable,0,128*sizeof(short));
memset(reversePitchTable,0,4096*sizeof(int));
memset(pitchTable,0,4096*sizeof(int));
memset(effectSlotMap,-1,4096*sizeof(short));
memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*));
memset(walked,0,8192);
memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*)));

View File

@ -130,6 +130,14 @@ enum DivSystem {
DIV_SYSTEM_PV1000
};
enum DivEffectType: unsigned short {
DIV_EFFECT_NULL=0,
DIV_EFFECT_DUMMY,
DIV_EFFECT_EXTERNAL,
DIV_EFFECT_VOLUME,
DIV_EFFECT_FILTER
};
struct DivGroovePattern {
unsigned char val[16];
unsigned char len;
@ -191,6 +199,21 @@ struct DivAssetDir {
name(n) {}
};
struct DivEffectStorage {
DivEffectType id;
unsigned short slot, storageVer;
float dryWet;
unsigned char* storage;
size_t storageLen;
DivEffectStorage():
id(DIV_EFFECT_NULL),
slot(0),
storageVer(0),
dryWet(1.0f),
storage(NULL),
storageLen(0) {}
};
struct DivSong {
// version number used for saving the song.
// Furnace will save using the latest possible version,
@ -366,6 +389,8 @@ struct DivSong {
std::vector<DivAssetDir> waveDir;
std::vector<DivAssetDir> sampleDir;
std::vector<DivEffectStorage> effects;
DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsOPLDrums, nullInsQSound;
DivWavetable nullWave;
DivSample nullSample;