Skip to content

Commit f537222

Browse files
committed
work on documenting the dispatch template
1 parent 7dc8a0e commit f537222

4 files changed

Lines changed: 419 additions & 8 deletions

File tree

src/engine/chipUtils.h

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,102 @@
2727
#include "macroInt.h"
2828

2929
// custom clock limits
30+
// these values are intentional. they prevent the user from overloading the engine by using extraneous clocks.
3031
#define MIN_CUSTOM_CLOCK 100000
3132
#define MAX_CUSTOM_CLOCK 40000000
3233

33-
// common shared channel struct
34+
/**
35+
* the SharedChannel struct holds common channel state, such as frequency, volume, note activity and so on.
36+
* this is used by almost every dispatch.
37+
*
38+
* create a struct inherited from SharedChannel in your dispatch's class definition:
39+
*
40+
* struct Channel: public SharedChannel<int> {
41+
* // state...
42+
* };
43+
*/
3444
template<typename T> struct SharedChannel {
45+
// freq: the output frequency (usually).
46+
// - this is calculated on frequency changes (freqChanged should be checked during tick()).
47+
// - the function that calculates frequency is DivEngine::calcFreq(). pass in the rest of variables
48+
// and you should get a frequency.
49+
// - certain chips require conversion of this frequency to some usable value
50+
// (e.g. SAA1099 has 8-bit divider and 3-bit octave selector).
51+
// baseFreq: frequency of the current note, including pitch slides.
52+
// - linear pitch: 8.7 fixed number. integer part is note and fractional part is pitch (in 128ths).
53+
// - non-linear pitch:
54+
// - this value is set during note changes. calculate it by using NOTE_FREQUENCY() or NOTE_PERIODIC().
55+
// - remember to set CHIP_DIVIDER/CHIP_FREQBASE in your dispatch's code!
56+
// baseNoteOverride: set when the arp macro's value is fixed. in that case, fixedArp will be true.
57+
// pitch: the pitch offset. set on DIV_CMD_PITCH (calculated from current E5xx and vibrato state).
58+
// pitch2: pitch macro's output.
59+
// arpOff: the arp macro's value (relative), in semitones.
3560
int freq, baseFreq, baseNoteOverride, pitch, pitch2, arpOff;
61+
// ins: current instrument. -1 is none/default.
62+
// note: current note, in semitones. 0 is C-0.
63+
// sampleNote: note in sample map.
64+
// sampleNoteDelta: difference between note and sampleNote, used in arp calculation, legato and pitch slides.
3665
int ins, note, sampleNote, sampleNoteDelta;
66+
// active: whether the note is currently on.
67+
// insChanged: whether an instrument change has occurred.
68+
// - if this is true, make sure to commit the new instrument during DIV_CMD_NOTE_ON and set this to false.
69+
// freqChanged: whether freq needs to be updated.
70+
// - should be set during DIV_CMD_NOTE_ON, DIV_CMD_NOTE_PORTA, DIV_CMD_LEGATO, DIV_CMD_PITCH and other
71+
// commands which alter pitch or baseFreq.
72+
// - should also be set by the arp macro handler.
73+
// - check for this variable on tick().
74+
// keyOn: whether there's a pending note on.
75+
// - the checks for freqChanged and keyOn are usually fused. see pce.cpp to see what I mean.
76+
// - check for this variable on tick().
77+
// keyOff: whether there's a pending note off.
78+
// - the checks for freqChanged and keyOff are usually fused. see pce.cpp to see what I mean.
79+
// - check for this variable on tick().
80+
// portaPause: used by the FM chips for compatibility.
81+
// - please pretend this variable doesn't exist...
82+
// inPorta: whether we currently are in a portamento.
83+
// - should be set during DIV_CMD_PRE_PORTA.
84+
// - in non-linear pitch, this variable is used to inhibit certain pitch changes during a pitch slide.
3785
bool active, insChanged, freqChanged, fixedArp, keyOn, keyOff, portaPause, inPorta;
86+
// vol: the current volume, set during DIV_CMD_VOLUME.
87+
// outVol: the *output* volume.
88+
// - this is the same as vol when we don't have a volume macro going on.
89+
// - otherwise it is the result of a calculation with vol and the volume macro's value.
90+
// - calculate this value by using VOL_SCALE_LINEAR()/VOL_SCALE_LOG() in tick().
91+
// the type of these two is usually int, but some chips use signed char.
3892
T vol, outVol;
93+
// std: this is the macro interpreter.
94+
// - the name comes from DefleMask, where macro-able instruments have "STD" type.
95+
// - don't laugh at me.
96+
// - initialize it during DIV_CMD_NOTE_ON. use the macroInit() helper.
97+
// - de-initialize it (by calling macroInit(NULL)) during DIV_CMD_NOTE_OFF.
98+
// - don't do this if your chip supports hardware envelopes!
99+
// - call mask() during DIV_CMD_MACRO_OFF/DIV_CMD_MACRO_ON.
100+
// - call restart() during DIV_CMD_MACRO_RESTART.
101+
// - call next() during tick().
102+
// - make sure to bind the engine during reset()! use setEngine().
103+
// - also remember to call notifyInsDeletion() on notifyInsDeletion().
104+
// - if you don't do this, you'll be referencing a potentially extinct instrument
105+
// and prompt Furnace to collapse.
39106
DivMacroInt std;
107+
108+
// here are some helper functions.
109+
/**
110+
* handle arpeggio. call during tick() like so:
111+
*
112+
* if (NEW_ARP_STRAT) {
113+
* chan[i].handleArp();
114+
* } else if (chan[i].std.arp.had) {
115+
* if (!chan[i].inPorta) {
116+
* chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
117+
* }
118+
* chan[i].freqChanged=true;
119+
* }
120+
*
121+
* the reason why we do it like that is because there are two arp strategies.
122+
* one of them allows you to run a pitch slide and arp macro simultaneously. the other one does not...
123+
* the latter is used in non-linear pitch or when the old arp strategy compat flag is enabled.
124+
* @param offset disregard. I don't remember what's this for.
125+
*/
40126
void handleArp(int offset=0) {
41127
if (std.arp.had) {
42128
if (std.arp.val<0) {
@@ -59,13 +145,24 @@ template<typename T> struct SharedChannel {
59145
freqChanged=true;
60146
}
61147
}
148+
/**
149+
* initializes the macro interpreter. call during DIV_CMD_NOTE_ON or DIV_CMD_NOTE_OFF (when applicable).
150+
* don't call std.init() directly! we need some other variables to be set as well.
151+
* @param which the instrument to read. set this to NULL to reset state.
152+
*/
62153
void macroInit(DivInstrument* which) {
63154
std.init(which);
64155
pitch2=0;
65156
arpOff=0;
66157
baseNoteOverride=0;
67158
fixedArp=false;
68159
}
160+
/**
161+
* call this constructor in your Channel's constructor, which should initialize the channel's state.
162+
* call your Channel's constructor during reset().
163+
*
164+
* @param initVol the initial channel volume.
165+
*/
69166
SharedChannel(T initVol):
70167
freq(0),
71168
baseFreq(0),

src/engine/dispatch.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,9 +786,12 @@ class DivMacroInt;
786786
* - runs macros (if necessary)
787787
* - performs register writes
788788
* - emulates a sound chip, synthesizes sound or otherwise provides an audible output to the engine
789-
* it is one of the vital components of Furnace.
789+
* it is one of the vital components of Furnace. it is referred to as "chip" in the UI.
790790
* it gets its name from the fact this is where commands are dispatched to.
791791
* this is not called DivChip because not all dispatches are chips (despite the UI calling them chips). an example is the Generic PCM DAC.
792+
*
793+
* implementations lie in platform/ and are prefixed with DivPlatform*.
794+
* check out platform/pce.cpp and platform/pce.h. these are templates I use frequently when adding new chips.
792795
*/
793796
class DivDispatch {
794797
protected:
@@ -1229,6 +1232,7 @@ class DivDispatch {
12291232
// - freqChanged: whether baseFreq and/or pitch have changed, and a frequency recalculation is required on the next tick.
12301233
// - the following definitions will help you calculate baseFreq.
12311234
// - to use them, define CHIP_DIVIDER and/or CHIP_FREQBASE in your code (not in the header though!).
1235+
// the value depends on the chip.
12321236
#define NOTE_PERIODIC(x) round(parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true))
12331237
#define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)
12341238
#define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false)

0 commit comments

Comments
 (0)