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+ */
3444template <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 ),
0 commit comments