|
35 | 35 |
|
36 | 36 | #include "spu/adsr.h"
|
37 | 37 |
|
| 38 | +#include <stdint.h> |
| 39 | + |
| 40 | +#include <utility> |
| 41 | + |
38 | 42 | #include "spu/externals.h"
|
39 | 43 | #include "spu/interface.h"
|
40 | 44 |
|
41 |
| -enum ADSRState : int32_t { |
42 |
| - Attack = 0, |
43 |
| - Decay = 1, |
44 |
| - Sustain = 2, |
45 |
| - Release = 3, |
| 45 | +namespace EnvelopeTables { |
| 46 | +// Generate ADSR envelope tables at compile time with some magic(thanks Nic) |
| 47 | +template <std::size_t N> |
| 48 | +struct Table { |
| 49 | + int32_t data[N]; |
46 | 50 | };
|
47 | 51 |
|
| 52 | +template <std::size_t N, typename Generator, std::size_t... Is> |
| 53 | +constexpr Table<N> generateTable(std::index_sequence<Is...>) { |
| 54 | + return {{Generator::calculateValue(Is)...}}; |
| 55 | +} |
| 56 | + |
| 57 | +template <std::size_t N, typename Generator> |
| 58 | +constexpr Table<N> generateTable() { |
| 59 | + return generateTable<N, Generator>(std::make_index_sequence<128>{}); |
| 60 | +} |
| 61 | + |
| 62 | +struct DenominatorGenerator { |
| 63 | + static constexpr int32_t calculateValue(std::size_t rate) { return (rate < 48) ? 1 : (1 << ((rate >> 2) - 11)); } |
| 64 | +}; |
| 65 | + |
| 66 | +struct NumeratorIncreaseGenerator { |
| 67 | + static constexpr int32_t calculateValue(std::size_t rate) { |
| 68 | + return (rate < 48) ? (7 - (rate & 3)) << (11 - (rate >> 2)) : (7 - (rate & 3)); |
| 69 | + } |
| 70 | +}; |
| 71 | + |
| 72 | +struct NumeratorDecreaseGenerator { |
| 73 | + static constexpr int32_t calculateValue(std::size_t rate) { |
| 74 | + return (rate < 48) ? (-8 + (rate & 3)) << (11 - (rate >> 2)) : (-8 + (rate & 3)); |
| 75 | + } |
| 76 | +}; |
| 77 | + |
| 78 | +constexpr auto denominator = generateTable<128, DenominatorGenerator>(); |
| 79 | +constexpr auto numerator_increase = generateTable<128, NumeratorIncreaseGenerator>(); |
| 80 | +constexpr auto numerator_decrease = generateTable<128, NumeratorDecreaseGenerator>(); |
| 81 | +} // namespace EnvelopeTables |
| 82 | + |
48 | 83 | inline int PCSX::SPU::ADSR::Attack(SPUCHAN *ch) {
|
49 |
| - uint32_t disp; |
| 84 | + int rate = ch->ADSRX.get<exAttackRate>().value; |
50 | 85 | int32_t EnvelopeVol = ch->ADSRX.get<exEnvelopeVol>().value;
|
| 86 | + int32_t EnvelopeVolF = ch->ADSRX.get<exEnvelopeVolF>().value; |
| 87 | + const int32_t attack_mode_exp = ch->ADSRX.get<exAttackModeExp>().value; |
51 | 88 |
|
52 |
| - if (ch->ADSRX.get<exAttackModeExp>().value && EnvelopeVol >= 0x60000000) { |
53 |
| - // Exponential Increase |
54 |
| - disp = -0x18 + 32; |
55 |
| - } else { |
56 |
| - // Linear Increase |
57 |
| - disp = -0x10 + 32; |
| 89 | + // Exponential increase |
| 90 | + if (attack_mode_exp && EnvelopeVol >= 0x6000) { |
| 91 | + rate += 8; |
58 | 92 | }
|
59 | 93 |
|
60 |
| - EnvelopeVol += m_table[ch->ADSRX.get<exAttackRate>().value + disp]; |
| 94 | + EnvelopeVolF++; |
| 95 | + if (EnvelopeVolF >= EnvelopeTables::denominator.data[rate]) { |
| 96 | + EnvelopeVolF = 0; |
| 97 | + EnvelopeVol += EnvelopeTables::numerator_increase.data[rate]; |
| 98 | + } |
61 | 99 |
|
62 |
| - if (EnvelopeVol < 0) { |
63 |
| - EnvelopeVol = 0x7FFFFFFF; |
| 100 | + if (EnvelopeVol >= 32767L) { |
| 101 | + EnvelopeVol = 32767L; |
64 | 102 | ch->ADSRX.get<exState>().value = ADSRState::Decay;
|
65 | 103 | }
|
66 | 104 |
|
67 | 105 | ch->ADSRX.get<exEnvelopeVol>().value = EnvelopeVol;
|
68 |
| - ch->ADSRX.get<exVolume>().value = (EnvelopeVol >>= 21); |
| 106 | + ch->ADSRX.get<exEnvelopeVolF>().value = EnvelopeVolF; |
| 107 | + ch->ADSRX.get<exVolume>().value = (EnvelopeVol >>= 5); |
| 108 | + |
69 | 109 | return EnvelopeVol;
|
70 | 110 | }
|
71 | 111 |
|
72 | 112 | inline int PCSX::SPU::ADSR::Decay(SPUCHAN *ch) {
|
73 |
| - uint32_t disp; |
| 113 | + const int rate = ch->ADSRX.get<exDecayRate>().value * 4; |
74 | 114 | int32_t EnvelopeVol = ch->ADSRX.get<exEnvelopeVol>().value;
|
| 115 | + int32_t EnvelopeVolF = ch->ADSRX.get<exEnvelopeVolF>().value; |
| 116 | + const int32_t release_mode_exp = ch->ADSRX.get<exReleaseModeExp>().value; |
75 | 117 |
|
76 |
| - disp = m_tableDisp[(EnvelopeVol >> 28) & 0x7]; |
77 |
| - EnvelopeVol -= m_table[ch->ADSRX.get<exDecayRate>().value + disp]; |
| 118 | + EnvelopeVolF++; |
| 119 | + if (EnvelopeVolF >= EnvelopeTables::denominator.data[rate]) { |
| 120 | + EnvelopeVolF = 0; |
78 | 121 |
|
79 |
| - if (EnvelopeVol < 0) EnvelopeVol = 0; |
| 122 | + if (release_mode_exp) { |
| 123 | + // Exponential decrease |
| 124 | + EnvelopeVol += (EnvelopeTables::numerator_decrease.data[rate] * EnvelopeVol) >> 15; |
| 125 | + } else { |
| 126 | + EnvelopeVol += EnvelopeTables::numerator_decrease.data[rate]; |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + if (EnvelopeVol < 0) { |
| 131 | + EnvelopeVol = 0; |
| 132 | + } |
80 | 133 |
|
81 |
| - // FF7 Cursor / Vagrant Story footsteps - use Neill's 4-bit accuracy |
82 |
| - if ((EnvelopeVol & 0x78000000) <= ch->ADSRX.get<exSustainLevel>().value) { |
| 134 | + if (((EnvelopeVol >> 11) & 0xf) <= ch->ADSRX.get<exSustainLevel>().value) { |
83 | 135 | ch->ADSRX.get<exState>().value = ADSRState::Sustain;
|
84 | 136 | }
|
85 | 137 |
|
86 | 138 | ch->ADSRX.get<exEnvelopeVol>().value = EnvelopeVol;
|
87 |
| - ch->ADSRX.get<exVolume>().value = (EnvelopeVol >>= 21); |
| 139 | + ch->ADSRX.get<exEnvelopeVolF>().value = EnvelopeVolF; |
| 140 | + ch->ADSRX.get<exVolume>().value = (EnvelopeVol >>= 5); |
| 141 | + |
88 | 142 | return EnvelopeVol;
|
89 | 143 | }
|
90 | 144 |
|
91 | 145 | inline int PCSX::SPU::ADSR::Sustain(SPUCHAN *ch) {
|
92 |
| - uint32_t disp; |
| 146 | + int rate = ch->ADSRX.get<exSustainRate>().value; |
93 | 147 | int32_t EnvelopeVol = ch->ADSRX.get<exEnvelopeVol>().value;
|
| 148 | + int32_t EnvelopeVolF = ch->ADSRX.get<exEnvelopeVolF>().value; |
| 149 | + const int32_t sustain_mode_exp = ch->ADSRX.get<exSustainModeExp>().value; |
| 150 | + const int32_t sustain_increase = ch->ADSRX.get<exSustainIncrease>().value; |
| 151 | + |
| 152 | + if (sustain_increase) { |
| 153 | + // Exponential increase |
| 154 | + if (sustain_mode_exp && (EnvelopeVol >= 0x6000)) { |
| 155 | + rate += 8; |
| 156 | + } |
94 | 157 |
|
95 |
| - if (ch->ADSRX.get<exSustainIncrease>().value) { |
96 |
| - disp = -0x10 + 32; |
97 |
| - if (ch->ADSRX.get<exSustainModeExp>().value) { |
98 |
| - if (EnvelopeVol >= 0x60000000) disp = -0x18 + 32; |
| 158 | + EnvelopeVolF++; |
| 159 | + if (EnvelopeVolF >= EnvelopeTables::denominator.data[rate]) { |
| 160 | + EnvelopeVolF = 0; |
| 161 | + EnvelopeVol += EnvelopeTables::numerator_increase.data[rate]; |
99 | 162 | }
|
100 |
| - EnvelopeVol += m_table[ch->ADSRX.get<exSustainRate>().value + disp]; |
101 | 163 |
|
102 |
| - if (EnvelopeVol < 0) { |
103 |
| - EnvelopeVol = 0x7FFFFFFF; |
| 164 | + if (EnvelopeVol > 32767L) { |
| 165 | + EnvelopeVol = 32767L; |
104 | 166 | }
|
| 167 | + |
105 | 168 | } else {
|
106 |
| - if (ch->ADSRX.get<exSustainModeExp>().value) { |
107 |
| - disp = m_tableDisp[((EnvelopeVol >> 28) & 0x7) + 8]; |
108 |
| - } else { |
109 |
| - disp = -0x0F + 32; |
| 169 | + EnvelopeVolF++; |
| 170 | + if (EnvelopeVolF >= EnvelopeTables::denominator.data[rate]) { |
| 171 | + EnvelopeVolF = 0; |
| 172 | + |
| 173 | + // Exponential decrease |
| 174 | + if (sustain_mode_exp) { |
| 175 | + EnvelopeVol += (EnvelopeTables::numerator_decrease.data[rate] * EnvelopeVol) >> 15; |
| 176 | + } else { |
| 177 | + EnvelopeVol += EnvelopeTables::numerator_decrease.data[rate]; |
| 178 | + } |
110 | 179 | }
|
111 |
| - EnvelopeVol -= m_table[ch->ADSRX.get<exSustainRate>().value + disp]; |
112 | 180 |
|
113 |
| - if (EnvelopeVol < 0) { |
| 181 | + if (EnvelopeVol < 0L) { |
114 | 182 | EnvelopeVol = 0;
|
115 | 183 | }
|
116 | 184 | }
|
| 185 | + |
117 | 186 | ch->ADSRX.get<exEnvelopeVol>().value = EnvelopeVol;
|
118 |
| - ch->ADSRX.get<exVolume>().value = (EnvelopeVol >>= 21); |
| 187 | + ch->ADSRX.get<exEnvelopeVolF>().value = EnvelopeVolF; |
| 188 | + ch->ADSRX.get<exVolume>().value = (EnvelopeVol >>= 5); |
| 189 | + |
119 | 190 | return EnvelopeVol;
|
120 | 191 | }
|
121 | 192 |
|
122 | 193 | inline int PCSX::SPU::ADSR::Release(SPUCHAN *ch) {
|
123 |
| - uint32_t disp; |
| 194 | + int rate = ch->ADSRX.get<exReleaseRate>().value * 4; |
124 | 195 | int32_t EnvelopeVol = ch->ADSRX.get<exEnvelopeVol>().value;
|
| 196 | + int32_t EnvelopeVolF = ch->ADSRX.get<exEnvelopeVolF>().value; |
| 197 | + const int32_t release_mode_exp = ch->ADSRX.get<exReleaseModeExp>().value; |
125 | 198 |
|
126 |
| - if (ch->ADSRX.get<exReleaseModeExp>().value) { |
127 |
| - disp = m_tableDisp[(EnvelopeVol >> 28) & 0x7]; |
128 |
| - } else { |
129 |
| - disp = -0x0C + 32; |
| 199 | + EnvelopeVolF++; |
| 200 | + if (EnvelopeVolF >= EnvelopeTables::denominator.data[rate]) { |
| 201 | + EnvelopeVolF = 0; |
| 202 | + |
| 203 | + // Exponential decrease |
| 204 | + if (release_mode_exp) { |
| 205 | + EnvelopeVol += (EnvelopeTables::numerator_decrease.data[rate] * EnvelopeVol) >> 15; |
| 206 | + } else { |
| 207 | + EnvelopeVol += EnvelopeTables::numerator_decrease.data[rate]; |
| 208 | + } |
130 | 209 | }
|
131 |
| - EnvelopeVol -= m_table[ch->ADSRX.get<exReleaseRate>().value + disp]; |
132 | 210 |
|
133 |
| - if (EnvelopeVol < 0) { |
| 211 | + if (EnvelopeVol < 0L) { |
| 212 | + ch->ADSRX.get<exState>().value = ADSRState::Stopped; |
134 | 213 | EnvelopeVol = 0;
|
135 | 214 | ch->data.get<Chan::On>().value = false;
|
136 | 215 | }
|
137 | 216 |
|
138 | 217 | ch->ADSRX.get<exEnvelopeVol>().value = EnvelopeVol;
|
139 |
| - ch->ADSRX.get<exVolume>().value = (EnvelopeVol >>= 21); |
140 |
| - return EnvelopeVol; |
141 |
| -} |
142 |
| - |
143 |
| -// Init ADSR |
144 |
| -PCSX::SPU::ADSR::Table::Table() { |
145 |
| - memset(m_table, 0, |
146 |
| - sizeof(uint32_t) * 160); // build the rate table according to Neill's rules (see at bottom of file) |
147 |
| - |
148 |
| - uint32_t r = 3; |
149 |
| - uint32_t rs = 1; |
150 |
| - uint32_t rd = 0; |
151 |
| - |
152 |
| - // we start at pos 32 with the real values... everything before is 0 |
153 |
| - for (int i = 32; i < 160; i++) { |
154 |
| - if (r < 0x3FFFFFFF) { |
155 |
| - r += rs; |
156 |
| - rd++; |
157 |
| - |
158 |
| - if (rd == 5) { |
159 |
| - rd = 1; |
160 |
| - rs *= 2; |
161 |
| - } |
162 |
| - } |
163 |
| - |
164 |
| - if (r > 0x3FFFFFFF) { |
165 |
| - r = 0x3FFFFFFF; |
166 |
| - } |
| 218 | + ch->ADSRX.get<exEnvelopeVolF>().value = EnvelopeVolF; |
| 219 | + ch->ADSRX.get<exVolume>().value = (EnvelopeVol >>= 5); |
167 | 220 |
|
168 |
| - m_table[i] = r; |
169 |
| - } |
| 221 | + return EnvelopeVol; |
170 | 222 | }
|
171 | 223 |
|
172 | 224 | void PCSX::SPU::ADSR::start(SPUCHAN *pChannel) // MIX ADSR
|
173 | 225 | {
|
174 | 226 | pChannel->ADSRX.get<exVolume>().value = 1; // and init some adsr vars
|
175 | 227 | pChannel->ADSRX.get<exState>().value = ADSRState::Attack;
|
176 | 228 | pChannel->ADSRX.get<exEnvelopeVol>().value = 0;
|
| 229 | + pChannel->ADSRX.get<exEnvelopeVolF>().value = 0; |
177 | 230 | }
|
178 | 231 |
|
179 | 232 | int PCSX::SPU::ADSR::mix(SPUCHAN *ch) {
|
|
0 commit comments