Reverb — HLE + bytecode
Reverb — HLE reconstruction and DSP bytecode
DSPHLE selector 0x0e · program image prog16_room_reverb_1 · Preview (topology decoded and reconstructed; most panel→cell role mappings are position-decoded).
comb+all-pass tank from decoded coeffs (decay 0x97, damping 0x93, ladders 0x98/0xA1, mix 0xA9); tail 6x over dry.
This page pairs the bytecode the effect runs on the NEC µPD6383GF with the high-level reconstruction that makes it audible in MAME. The reconstruction is not the chip’s microcode — it is a textbook DSP block built from the decoded meaning of that microcode, and reading the two together is how the bytecode is understood (and, in time, driven toward a faithful low-level emulation). See also the signal-flow flowchart.
The bytecode below is the source of truth; the HLE reconstruction is not. The HLE is our best current interpretation and may contain mistakes — where the two disagree, the bytecode wins, and a better HLE should be updated here. This page is the permanent archive of the reference HLE: it is kept here even after the code is eventually removed from the MAME sources.
DSP bytecode (reverse-engineered microprogram)
The disassembled image the chip executes for this effect. Source (regenerable):
dsp/disasm/prog16_room_reverb_1.dsm.
; Reverse-engineered disassembly of the KN5000 effects-DSP microcode
; (NEC uPD6383GF), recovered from the original firmware. The microcode is the
; work of its original authors; this is a disassembly for preservation and
; interoperability, and no claim of copyright is made over the disassembled program.
; KN5000 effects-DSP program -- ROOM REVERB 1
; image rep algo 16 | slots 16,17,18,19,20,21,22,23,24,25,26,27 | unit 1 (I-RAM load 200)
; family reverb | confidence SOLVED | 133 words, 33 class-A multiplies (33 named)
; role: reverb tank: all-pass diffuser ladders of 5 and 4 stages + damping (algorithm decoded to the bit; the per-word roles of the 6-word core are narrowed to two surviving assignments); the ONLY unit-1 image, shared by the 12 reverb presets algos 16-27
; coefficient cursor base 0x90
;
; GENERATED by dsp/tools/gen_dsp_disasm.py -- DO NOT EDIT.
; Put labels/comments in the matching dsp/sym/*.sym; analysis in dsp/algorithms/.
ReverbBody:
w0 088013000B ?word 0x088013000B ; 880.1.30.00B hi12{ESC ?7 res=080} [external delay-DRAM READ (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); this end moves with the user's DELAY (ms) knob, and the delay is READ_CELL - WRITE_CELL; addr8 0x30 also marks the FIRST DRAM access of a body, 37 of 38 distinct images (R3 sect. 6.2). external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
; unit-1 body, loads at I-RAM 200; 133 words, 33 class-A multiplies
w1 0000289415 ld acc,(p)-119
w2 0212A811D5 mac (p),c+,(p)-127 ; mem[p]<-acc, acc=0
; C-RAM[0x90] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x90] = input scaling (role input-gain, PROVEN)
w3 0202AFD1D5 mac (p),c+,(p)-3
; C-RAM[0x91] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x91] = input scaling (role input-gain, PROVEN)
w4 0202AF91D5 mac (p),c+,(p)-7
; C-RAM[0x92] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x92] = input scaling (role input-gain, PROVEN)
w5 020224B1CD mac (p),(p)+75
w6 000020040E ld acc,(p)+0
w7 0212A001D5 mac (p),c+,(p)+0 ; mem[p]<-acc, acc=0
; C-RAM[0x93] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x93] = damping filter #1 (HIGH DAMP GAIN) (role damping, PROVEN)
w8 0212A00415 mac acc,c+,(p)+0 ; mem[p]<-acc, acc=0
; C-RAM[0x94] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x94] = damping filter #1 (role damping, PROVEN)
w9 0202A001D5 mac (p),c+,(p)+0
; C-RAM[0x95] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x95] = damping filter #1 (role damping, PROVEN)
w10 0202200407 mac.st acc,(p)+0
Separator0:
w11 08801602DA ?word 0x08801602DA ; 880.1.60.2DA hi12{ESC ?7 res=080} [external delay-DRAM WRITE (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); the line BASE -- MULTI TAP DELAY's four taps share exactly one of these, which is what forces the polarity. external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
; 5-word ladder separator: 880.1.60.2DA | 000.A.00.695 | X | 212.2.00.419 | 880.1.20.64B
w12 0000A00695 ld tb,c+,(p)+0
; C-RAM[0x96] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x96] = DRAM tap gain (0.500) (role decay, PROVEN)
; consumes C-RAM[0x96] = DRAM tap gain 0.500 -- NOT a diffuser gain
w13 00002BA000 nop
w14 0212200419 mac.ta2 acc,(p)+0 ; mem[p]<-acc, acc=0
w15 088012064B ?word 0x088012064B ; 880.1.20.64B hi12{ESC ?7 res=080} [external delay-DRAM READ (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); this end moves with the user's DELAY (ms) knob, and the delay is READ_CELL - WRITE_CELL. external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
w16 0000200000 nop
w17 0000A0A1D5 ld (p),c+,(p)+10
; C-RAM[0x97] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x97] = op0x75 reverb decay coeff (role decay, PROVEN)
w18 0202200000 mac.b (p)0,(p)+0
Ladder0:
w19 08801602D4 dly.w dsc[k],p+96
; DIFFUSER LADDER 0 -- 5 all-pass cores, C-RAM 0x98..0x9C
w20 0104200000 post.b (p)0,(p)+0
w21 0000200419 ld.ta2 acc,(p)+0
w22 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w23 0880120655 dly.r dsc[k],p+32
w24 0102A0064B mac ta,c+,(p)+0
; C-RAM[0x98] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x98] = diffuser ladder-0 (REVERB TIME) (role decay, PROVEN)
w25 0000200000 nop
w26 0000200000 nop
w27 08801602D4 dly.w dsc[k],p+96
; all-pass core 2 of 5
w28 0104200000 post.b (p)0,(p)+0
w29 0000200419 ld.ta2 acc,(p)+0
w30 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w31 0880120655 dly.r dsc[k],p+32
w32 0102A0064B mac ta,c+,(p)+0
; C-RAM[0x99] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x99] = diffuser ladder-0 (role decay, PROVEN)
w33 0000200000 nop
w34 0000200000 nop
w35 08801602D4 dly.w dsc[k],p+96
; all-pass core 3 of 5
w36 0104200000 post.b (p)0,(p)+0
w37 0000200419 ld.ta2 acc,(p)+0
w38 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w39 0880120655 dly.r dsc[k],p+32
w40 0102A0064B mac ta,c+,(p)+0
; C-RAM[0x9A] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x9A] = diffuser ladder-0 (role decay, PROVEN)
w41 0000200000 nop
w42 0000200000 nop
w43 08801602D4 dly.w dsc[k],p+96
; all-pass core 4 of 5
w44 0104200000 post.b (p)0,(p)+0
w45 0000200419 ld.ta2 acc,(p)+0
w46 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w47 0880120655 dly.r dsc[k],p+32
w48 0102A0064B mac ta,c+,(p)+0
; C-RAM[0x9B] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x9B] = diffuser ladder-0 (role decay, PROVEN)
w49 0000200000 nop
w50 0000200000 nop
w51 08801602D4 dly.w dsc[k],p+96
; all-pass core 5 of 5
w52 0104200000 post.b (p)0,(p)+0
w53 0000200419 ld.ta2 acc,(p)+0
w54 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w55 0880120655 dly.r dsc[k],p+32
w56 0102A0064B mac ta,c+,(p)+0
; C-RAM[0x9C] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x9C] = diffuser ladder-0 (role decay, PROVEN)
w57 0000200000 nop
w58 0000200000 nop
Separator1:
w59 08801602DA ?word 0x08801602DA ; 880.1.60.2DA hi12{ESC ?7 res=080} [external delay-DRAM WRITE (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); the line BASE -- MULTI TAP DELAY's four taps share exactly one of these, which is what forces the polarity. external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
; same 5-word shape as w11
w60 0000A00695 ld tb,c+,(p)+0
; C-RAM[0x9D] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x9D] = DRAM tap gain (0.500) (role decay, PROVEN)
; consumes C-RAM[0x9D] = DRAM tap gain 0.500
w61 00002F3407 ld.st acc,(p)-13
w62 0212200419 mac.ta2 acc,(p)+0 ; mem[p]<-acc, acc=0
w63 088012064B ?word 0x088012064B ; 880.1.20.64B hi12{ESC ?7 res=080} [external delay-DRAM READ (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); this end moves with the user's DELAY (ms) knob, and the delay is READ_CELL - WRITE_CELL. external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
w64 000024A407 ld.st acc,(p)+74
w65 0000A001D5 ld (p),c+,(p)+0
; C-RAM[0x9E] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x9E] = op0x76 damping triple #2 (HIGH DAMP GAIN) (role damping, PROVEN)
w66 0212A00415 mac acc,c+,(p)+0 ; mem[p]<-acc, acc=0
; C-RAM[0x9F] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0x9F] = damping triple #2 (role damping, PROVEN)
w67 0202A001D5 mac (p),c+,(p)+0
; C-RAM[0xA0] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xA0] = damping triple #2 (role damping, PROVEN)
w68 0202200407 mac.st acc,(p)+0
Ladder1:
w69 08801602D4 dly.w dsc[k],p+96
; DIFFUSER LADDER 1 -- FOUR all-pass cores, C-RAM 0xA1..0xA4
w70 0104200000 post.b (p)0,(p)+0
w71 0000200419 ld.ta2 acc,(p)+0
w72 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w73 0880120655 dly.r dsc[k],p+32
w74 0102ABA64B mac ta,c+,(p)-70
; C-RAM[0xA1] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xA1] = diffuser ladder-1 (REVERB TIME) (role decay, PROVEN)
; the ONE core word whose addr8 differs (0xBA), in all 12 presets
w75 0000200000 nop
w76 0000200000 nop
w77 08801602D4 dly.w dsc[k],p+96
; all-pass core 2 of 4
w78 0104200000 post.b (p)0,(p)+0
w79 0000200419 ld.ta2 acc,(p)+0
w80 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w81 0880120655 dly.r dsc[k],p+32
w82 0102A0064B mac ta,c+,(p)+0
; C-RAM[0xA2] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xA2] = diffuser ladder-1 (role decay, PROVEN)
w83 0000200000 nop
w84 0000200000 nop
w85 08801602D4 dly.w dsc[k],p+96
; all-pass core 3 of 4
w86 0104200000 post.b (p)0,(p)+0
w87 0000200419 ld.ta2 acc,(p)+0
w88 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w89 0880120655 dly.r dsc[k],p+32
w90 0102A0064B mac ta,c+,(p)+0
; C-RAM[0xA3] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xA3] = diffuser ladder-1 (role decay, PROVEN)
w91 0000200000 nop
w92 0000200000 nop
w93 08801602D4 dly.w dsc[k],p+96
; all-pass core 4 of 4
w94 0104200000 post.b (p)0,(p)+0
w95 0000200419 ld.ta2 acc,(p)+0
w96 0012200680 mac.b tb,(p)+0 ; mem[p]<-acc, acc=0
w97 0880120655 dly.r dsc[k],p+32
w98 0102A0064B mac ta,c+,(p)+0
; C-RAM[0xA4] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xA4] = diffuser ladder-1 (role decay, PROVEN)
w99 0000200000 nop
w100 0000200000 nop
Separator2:
w101 08801602DA ?word 0x08801602DA ; 880.1.60.2DA hi12{ESC ?7 res=080} [external delay-DRAM WRITE (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); the line BASE -- MULTI TAP DELAY's four taps share exactly one of these, which is what forces the polarity. external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
; same 5-word shape as w11
w102 0000A00695 ld tb,c+,(p)+0
; C-RAM[0xA5] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xA5] = DRAM tap gain (0.500) (role decay, PROVEN)
; consumes C-RAM[0xA5] = DRAM tap gain 0.500
w103 00002FE407 ld.st acc,(p)-2
w104 0212200419 mac.ta2 acc,(p)+0 ; mem[p]<-acc, acc=0
w105 088012064B ?word 0x088012064B ; 880.1.20.64B hi12{ESC ?7 res=080} [external delay-DRAM READ (FORCED, adjudication-round5 sect. 3 -- addr8 bit 6 is the direction field and 0x60 is the WRITE; this REVERSES R1 F1, which bounded the read latency to one repetition when the descriptors need twenty words); this end moves with the user's DELAY (ms) knob, and the delay is READ_CELL - WRITE_CELL. external delay-DRAM access; address = DESCRIPTOR_CELL[k] + G, from the host bank behind pointer ...825 / tag 0x4C (R3, PROVEN BY CONSTRUCTION) -- the k-th class-1 escape word of a body takes the k-th cell of that body's own descriptor block (the IDENTITY map, FORCED in adjudication-round5 sect. 1), so the address is NOT in this word]
w106 0000249407 ld.st acc,(p)+73
w107 0090A001D5 ld (p),c+,(p)+0 ; store SUPPRESSED (bit7)
; C-RAM[0xA6] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xA6] = op0x76 damping triple #3 (HIGH DAMP GAIN) (role damping, PROVEN)
w108 0212A00415 mac acc,c+,(p)+0 ; mem[p]<-acc, acc=0
; C-RAM[0xA7] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xA7] = damping triple #3 (role damping, PROVEN)
w109 0202A001D5 mac (p),c+,(p)+0
; C-RAM[0xA8] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xA8] = damping triple #3 (role damping, PROVEN)
w110 02022B8407 mac.st acc,(p)-72
w111 0C40180000 ldreg r00,#12 ; immediate -> the register lo12 selects
w112 0C40180000 ldreg r00,#12 ; immediate -> the register lo12 selects
w113 00002FE407 ld.st acc,(p)-2
w114 08801202D5 dly.r dsc[k],p+32
OutputTails:
w115 0282A00000 mac.b (p)0,c+,(p)+0
; C-RAM[0xA9] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xA9] = LEFT output tail (op0x66 / ER.LEVEL) (role mix, PROVEN)
; mirrored LEFT / RIGHT output tails, C-RAM 0xA9..0xB4
w116 0000A0C452 ?word 0x0000A0C452 ; 000.A.0C.452 hi12{-} cur+ [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
; C-RAM[0xAA] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xAA] = LEFT output tail (role mix, PROVEN)
w117 0212AF51D5 mac (p),c+,(p)-11 ; mem[p]<-acc, acc=0
; C-RAM[0xAB] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xAB] = LEFT output tail (role mix, PROVEN)
w118 0202AFC1D5 mac (p),c+,(p)-4
; C-RAM[0xAC] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xAC] = LEFT output tail (role mix, PROVEN)
w119 02022081CD mac (p),(p)+8
w120 00902FB40E ld acc,(p)-5 ; store SUPPRESSED (bit7)
w121 0212205000 mac.b (p)0,(p)+5 ; mem[p]<-acc, acc=0
w122 0C40180000 ldreg r00,#12 ; immediate -> the register lo12 selects
w123 0C40180000 ldreg r00,#12 ; immediate -> the register lo12 selects
w124 00002FB407 ld.st acc,(p)-5
w125 08801202D5 dly.r dsc[k],p+32
w126 0282A00000 mac.b (p)0,c+,(p)+0
; C-RAM[0xAD] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xAD] = RIGHT output tail (role mix, PROVEN)
w127 0000AFF452 ?word 0x0000AFF452 ; 000.A.FF.452 hi12{-} cur+ [SPECULATIVE (prospective, not measured): SRC 0x11 = ACCB (2nd accumulator)]
; C-RAM[0xAE] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xAE] = RIGHT output tail (role mix, PROVEN)
w128 0212A041D5 mac (p),c+,(p)+4 ; mem[p]<-acc, acc=0
; C-RAM[0xAF] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xAF] = RIGHT output tail (op0x66) (role mix, PROVEN)
w129 0202AFA1D5 mac (p),c+,(p)-6
; C-RAM[0xB0] (coeff, base 0x90 MEASURED)
; coeff C-RAM[0xB0] = RIGHT output tail (role mix, PROVEN)
w130 020227B1CD mac (p),(p)+123
w131 088016040E dly.w dsc[k],p+96
w132 061210F000 endblk #0F ; END OF BLOCK -- the last word of a block
HLE reconstruction (MAME, kn5000_tonegen.cpp)
The decode of the bytecode above, rebuilt as audible DSP. Two parts: the parameter refresh
(reads the decoded C-RAM coefficient cells once per update) and the per-sample insert (the
signal processing). Default OFF, behind the DSPHLE research port. Source:
src/mame/matsushita/kn5000_tonegen.cpp.
Parameter refresh (decode the C-RAM coefficients):
const bool rv_hle = (dsphle == 14);
double rv_fb = 0.0, rv_wet = 0.0, rv_apg[2] = { 0.5, 0.5 };
if (rv_hle)
{
auto q22v = [](u32 v) -> double {
int32_t s = (v & 0x800000) ? int32_t(v) - 0x1000000 : int32_t(v);
return double(s) / 4194304.0; };
const bool bit1 = (dspcfg & 2) != 0;
const double cs = bit1 ? 1.0 : 2.0;
// decay coeff 0x97 (REVERB TIME) -> a plausible comb feedback 0.6..0.92 that rises with it
const double decay = std::clamp(q22v(m_dsp1->cram_read(0x97)) * cs, 0.0, 1.0); // ~0.4 default
rv_fb = std::clamp(0.55 + 0.6 * decay, 0.55, 0.84); // bounded for a stable decay
// damping one-pole from the decoded HIGH-DAMP cell 0x93 (darker tails as it rises)
const double damp = std::clamp(q22v(m_dsp1->cram_read(0x93)) * cs * 0.5, 0.0, 0.7);
for (int i = 0; i < 4; i++) { m_rv_d_l[i].set_damping(damp); m_rv_d_r[i].set_damping(damp); }
// two all-pass diffuser gains from the decoded ladder (operand scale, kept in range)
rv_apg[0] = std::clamp(q22v(m_dsp1->cram_read(0x98)) * cs * 0.5, 0.3, 0.75);
rv_apg[1] = std::clamp(q22v(m_dsp1->cram_read(0xA1)) * cs * 0.5, 0.3, 0.75);
rv_wet = std::clamp(q22v(m_dsp1->cram_read(0xA9)) * cs, 0.15, 0.6); // output-tail mix
}
Per-sample insert (the reconstructed signal path):
if (rv_hle)
{
const int CD_L[4] = { 1557, 1617, 1693, 1781 }, CD_R[4] = { 1579, 1639, 1711, 1801 };
const int AD_L[2] = { 556, 225 }, AD_R[2] = { 579, 241 };
const double sl = double(mix_l) / 32768.0, sr = double(mix_r) / 32768.0;
// small input gain into the comb bank so the combs' recirculation gain 1/(1-fb)
// does not blow the level up (Freeverb's fixed-gain trick)
const double il = 1.2 * m_rv_pre_l.read(1800.0), ir = 1.2 * m_rv_pre_r.read(1800.0);
m_rv_pre_l.write(sl); m_rv_pre_r.write(sr);
double xl = 0.0, xr = 0.0;
for (int c = 0; c < 4; c++) // parallel comb bank (each recirculates the input)
{
const double tl = m_rv_c_l[c].read(double(CD_L[c])), tr = m_rv_c_r[c].read(double(CD_R[c]));
m_rv_c_l[c].write(il + rv_fb * m_rv_d_l[c].process_one(tl));
m_rv_c_r[c].write(ir + rv_fb * m_rv_d_r[c].process_one(tr));
xl += tl; xr += tr;
}
for (int a = 0; a < 2; a++) // unity-gain Schroeder all-pass diffusers
{
const double dl = m_rv_a_l[a].read(double(AD_L[a])), dr = m_rv_a_r[a].read(double(AD_R[a]));
const double yl = -rv_apg[a] * xl + dl, yr = -rv_apg[a] * xr + dr;
m_rv_a_l[a].write(xl + rv_apg[a] * yl);
m_rv_a_r[a].write(xr + rv_apg[a] * yr);
xl = yl; xr = yr;
}
mix_l = int32_t(((1.0 - rv_wet) * sl + rv_wet * xl) * 32768.0);
mix_r = int32_t(((1.0 - rv_wet) * sr + rv_wet * xr) * 32768.0);
}