From 2da1fe8821e6f39d6fe5721964cee779c2253a5c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 21 May 2023 04:39:36 -0500 Subject: [PATCH] prepare for patchbay effects --- CMakeLists.txt | 3 + papers/format.md | 10 +- src/engine/effect.h | 189 +++++++++++++++++++++++++++++++++ src/engine/effect/abstract.cpp | 85 +++++++++++++++ src/engine/effect/dummy.cpp | 60 +++++++++++ src/engine/effect/dummy.h | 34 ++++++ src/engine/effectContainer.cpp | 95 +++++++++++++++++ src/engine/engine.h | 32 ++++++ src/engine/song.h | 25 +++++ 9 files changed, 528 insertions(+), 5 deletions(-) create mode 100644 src/engine/effect.h create mode 100644 src/engine/effect/abstract.cpp create mode 100644 src/engine/effect/dummy.cpp create mode 100644 src/engine/effect/dummy.h create mode 100644 src/engine/effectContainer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 78a68aa83..3e2f5ee5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/papers/format.md b/papers/format.md index b22d7b2c7..198f62648 100644 --- a/papers/format.md +++ b/papers/format.md @@ -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 diff --git a/src/engine/effect.h b/src/engine/effect.h new file mode 100644 index 000000000..5c735d31d --- /dev/null +++ b/src/engine/effect.h @@ -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 +#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 + * + +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() { +} \ No newline at end of file diff --git a/src/engine/effect/dummy.cpp b/src/engine/effect/dummy.cpp new file mode 100644 index 000000000..13df4f3f5 --- /dev/null +++ b/src/engine/effect/dummy.cpp @@ -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() { +} \ No newline at end of file diff --git a/src/engine/effect/dummy.h b/src/engine/effect/dummy.h new file mode 100644 index 000000000..c8a42919d --- /dev/null +++ b/src/engine/effect/dummy.h @@ -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(); +}; \ No newline at end of file diff --git a/src/engine/effectContainer.cpp b/src/engine/effectContainer.cpp new file mode 100644 index 000000000..d51ddfe57 --- /dev/null +++ b/src/engine/effectContainer.cpp @@ -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 (inLengetOutputCount(); + + if (outLenacquire(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 midiOuts; std::vector cmdStream; std::vector possibleInsTypes; + std::vector 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*))); diff --git a/src/engine/song.h b/src/engine/song.h index aff14f48f..fd2d1e07d 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -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 waveDir; std::vector sampleDir; + std::vector effects; + DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsOPLDrums, nullInsQSound; DivWavetable nullWave; DivSample nullSample;