diff options
Diffstat (limited to 'lib/rbcodec/codecs/libgme/emuadpcm.c')
-rw-r--r-- | lib/rbcodec/codecs/libgme/emuadpcm.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/emuadpcm.c b/lib/rbcodec/codecs/libgme/emuadpcm.c new file mode 100644 index 0000000000..b426a74de5 --- /dev/null +++ b/lib/rbcodec/codecs/libgme/emuadpcm.c | |||
@@ -0,0 +1,297 @@ | |||
1 | /* | ||
2 | * This file is based on: | ||
3 | * Y8950Adpcm.cc -- Y8950 ADPCM emulator from the openMSX team | ||
4 | * ported to c by gama | ||
5 | * | ||
6 | * The openMSX version is based on: | ||
7 | * emuadpcm.c -- Y8950 ADPCM emulator written by Mitsutaka Okazaki 2001 | ||
8 | * heavily rewritten to fit openMSX structure | ||
9 | */ | ||
10 | |||
11 | #include <string.h> | ||
12 | |||
13 | #include "emuadpcm.h" | ||
14 | #include "emu8950.h" | ||
15 | |||
16 | // Relative volume between ADPCM part and FM part, | ||
17 | // value experimentally found by Manuel Bilderbeek | ||
18 | const int ADPCM_VOLUME = 356; | ||
19 | |||
20 | // Bitmask for register 0x07 | ||
21 | static const int R07_RESET = 0x01; | ||
22 | //static const int R07 = 0x02;. // not used | ||
23 | //static const int R07 = 0x04;. // not used | ||
24 | const int R07_SP_OFF = 0x08; | ||
25 | const int R07_REPEAT = 0x10; | ||
26 | const int R07_MEMORY_DATA = 0x20; | ||
27 | const int R07_REC = 0x40; | ||
28 | const int R07_START = 0x80; | ||
29 | |||
30 | //Bitmask for register 0x08 | ||
31 | const int R08_ROM = 0x01; | ||
32 | const int R08_64K = 0x02; | ||
33 | const int R08_DA_AD = 0x04; | ||
34 | const int R08_SAMPL = 0x08; | ||
35 | //const int R08 = 0x10;. // not used | ||
36 | //const int R08 = 0x20;. // not used | ||
37 | const int R08_NOTE_SET = 0x40; | ||
38 | const int R08_CSM = 0x80; | ||
39 | |||
40 | const int DMAX = 0x6000; | ||
41 | const int DMIN = 0x7F; | ||
42 | const int DDEF = 0x7F; | ||
43 | |||
44 | const int DECODE_MAX = 32767; | ||
45 | const int DECODE_MIN = -32768; | ||
46 | |||
47 | #define GETA_BITS 14 | ||
48 | #define MAX_STEP (1<<(16+GETA_BITS)) | ||
49 | |||
50 | |||
51 | //**************************************************// | ||
52 | // // | ||
53 | // Helper functions // | ||
54 | // // | ||
55 | //**************************************************// | ||
56 | |||
57 | static int CLAP(int min, int x, int max) | ||
58 | { | ||
59 | return (x < min) ? min : ((max < x) ? max : x); | ||
60 | } | ||
61 | |||
62 | //**********************************************************// | ||
63 | // // | ||
64 | // Y8950Adpcm // | ||
65 | // // | ||
66 | //**********************************************************// | ||
67 | |||
68 | |||
69 | void ADPCM_init(struct Y8950Adpcm* this_, struct Y8950* y8950_, byte* ramBank, int sampleRam) | ||
70 | |||
71 | { | ||
72 | this_->y8950 = y8950_; | ||
73 | this_->ramBank = ramBank; | ||
74 | this_->ramSize = sampleRam; | ||
75 | memset(this_->ramBank, 0xFF, this_->ramSize); | ||
76 | this_->volume = 0; | ||
77 | } | ||
78 | |||
79 | void restart(struct Y8950Adpcm* this_); | ||
80 | void ADPCM_reset(struct Y8950Adpcm* this_) | ||
81 | { | ||
82 | this_->playing = false; | ||
83 | this_->startAddr = 0; | ||
84 | this_->stopAddr = 7; | ||
85 | this_->memPntr = 0; | ||
86 | this_->delta = 0; | ||
87 | this_->step = 0; | ||
88 | this_->addrMask = (1 << 19) - 1; | ||
89 | this_->reg7 = 0; | ||
90 | this_->reg15 = 0; | ||
91 | ADPCM_writeReg(this_, 0x12, 255); // volume | ||
92 | restart(this_); | ||
93 | } | ||
94 | |||
95 | void ADPCM_setSampleRate(struct Y8950Adpcm* this_, int sr, int clk) | ||
96 | { | ||
97 | this_->sampleRate = sr; | ||
98 | this_->clockRate = clk; | ||
99 | } | ||
100 | |||
101 | bool ADPCM_muted(struct Y8950Adpcm* this_) | ||
102 | { | ||
103 | return (!this_->playing) || (this_->reg7 & R07_SP_OFF); | ||
104 | } | ||
105 | |||
106 | //**************************************************// | ||
107 | // // | ||
108 | // I/O Ctrl // | ||
109 | // // | ||
110 | //**************************************************// | ||
111 | |||
112 | void restart(struct Y8950Adpcm* this_) | ||
113 | { | ||
114 | this_->playAddr = this_->startAddr & this_->addrMask; | ||
115 | this_->nowStep = MAX_STEP - this_->step; | ||
116 | this_->out = this_->output = 0; | ||
117 | this_->diff = DDEF; | ||
118 | this_->nextLeveling = 0; | ||
119 | this_->sampleStep = 0; | ||
120 | this_->volumeWStep = (int)((long long)this_->volume * this_->step / MAX_STEP); | ||
121 | } | ||
122 | |||
123 | void ADPCM_writeReg(struct Y8950Adpcm* this_, byte rg, byte data) | ||
124 | { | ||
125 | switch (rg) { | ||
126 | case 0x07: // START/REC/MEM DATA/REPEAT/SP-OFF/-/-/RESET | ||
127 | this_->reg7 = data; | ||
128 | if (this_->reg7 & R07_RESET) { | ||
129 | this_->playing = false; | ||
130 | } else if (data & R07_START) { | ||
131 | this_->playing = true; | ||
132 | restart(this_); | ||
133 | } | ||
134 | break; | ||
135 | |||
136 | case 0x08: // CSM/KEY BOARD SPLIT/-/-/SAMPLE/DA AD/64K/ROM | ||
137 | this_->romBank = data & R08_ROM; | ||
138 | this_->addrMask = data & R08_64K ? (1<<17)-1 : (1<<19)-1; | ||
139 | break; | ||
140 | |||
141 | case 0x09: // START ADDRESS (L) | ||
142 | this_->startAddr = (this_->startAddr & 0x7F800) | (data << 3); | ||
143 | this_->memPntr = 0; | ||
144 | break; | ||
145 | case 0x0A: // START ADDRESS (H) | ||
146 | this_->startAddr = (this_->startAddr & 0x007F8) | (data << 11); | ||
147 | this_->memPntr = 0; | ||
148 | break; | ||
149 | |||
150 | case 0x0B: // STOP ADDRESS (L) | ||
151 | this_->stopAddr = (this_->stopAddr & 0x7F807) | (data << 3); | ||
152 | break; | ||
153 | case 0x0C: // STOP ADDRESS (H) | ||
154 | this_->stopAddr = (this_->stopAddr & 0x007FF) | (data << 11); | ||
155 | break; | ||
156 | |||
157 | |||
158 | case 0x0F: // ADPCM-DATA | ||
159 | // TODO check this | ||
160 | //if ((reg7 & R07_REC) && (reg7 & R07_MEMORY_DATA)) { | ||
161 | { | ||
162 | int tmp = ((this_->startAddr + this_->memPntr) & this_->addrMask) / 2; | ||
163 | tmp = (tmp < this_->ramSize) ? tmp : (tmp & (this_->ramSize - 1)); | ||
164 | if (!this_->romBank) { | ||
165 | this_->ramBank[tmp] = data; | ||
166 | } | ||
167 | //PRT_DEBUG("Y8950Adpcm: mem " << tmp << " " << (int)data); | ||
168 | this_->memPntr += 2; | ||
169 | if ((this_->startAddr + this_->memPntr) > this_->stopAddr) { | ||
170 | OPL_setStatus(this_->y8950, STATUS_EOS); | ||
171 | } | ||
172 | } | ||
173 | OPL_setStatus(this_->y8950, STATUS_BUF_RDY); | ||
174 | break; | ||
175 | |||
176 | case 0x10: // DELTA-N (L) | ||
177 | this_->delta = (this_->delta & 0xFF00) | data; | ||
178 | this_->step = rate_adjust(this_->delta<<GETA_BITS, this_->sampleRate, this_->clockRate); | ||
179 | this_->volumeWStep = (int)((long long)this_->volume * this_->step / MAX_STEP); | ||
180 | break; | ||
181 | case 0x11: // DELTA-N (H) | ||
182 | this_->delta = (this_->delta & 0x00FF) | (data << 8); | ||
183 | this_->step = rate_adjust(this_->delta<<GETA_BITS, this_->sampleRate, this_->clockRate); | ||
184 | this_->volumeWStep = (int)((long long)this_->volume * this_->step / MAX_STEP); | ||
185 | break; | ||
186 | |||
187 | case 0x12: { // ENVELOP CONTROL | ||
188 | int oldVol = this_->volume; | ||
189 | this_->volume = (data * ADPCM_VOLUME) >> 8; | ||
190 | if (oldVol != 0) { | ||
191 | this_->output = (int)(((long long)this_->output * this_->volume) / oldVol); | ||
192 | this_->sampleStep = (int)(((long long)this_->sampleStep * this_->volume) / oldVol); | ||
193 | } | ||
194 | this_->volumeWStep = (int)((long long)this_->volume * this_->step / MAX_STEP); | ||
195 | break; | ||
196 | } | ||
197 | case 0x0D: // PRESCALE (L) | ||
198 | case 0x0E: // PRESCALE (H) | ||
199 | case 0x15: // DAC-DATA (bit9-2) | ||
200 | case 0x16: // (bit1-0) | ||
201 | case 0x17: // (exponent) | ||
202 | case 0x1A: // PCM-DATA | ||
203 | // not implemented | ||
204 | break; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | byte ADPCM_readReg(struct Y8950Adpcm* this_, byte rg) | ||
209 | { | ||
210 | byte result; | ||
211 | switch (rg) { | ||
212 | case 0x0F: { // ADPCM-DATA | ||
213 | // TODO don't advance pointer when playing??? | ||
214 | int adr = ((this_->startAddr + this_->memPntr) & this_->addrMask) / 2; | ||
215 | if (this_->romBank || (adr >= this_->ramSize)) { | ||
216 | result = 0xFF; | ||
217 | } else { | ||
218 | result = this_->ramBank[adr]; | ||
219 | } | ||
220 | this_->memPntr += 2; | ||
221 | if ((this_->startAddr + this_->memPntr) > this_->stopAddr) { | ||
222 | OPL_setStatus(this_->y8950, STATUS_EOS); | ||
223 | } | ||
224 | break; | ||
225 | } | ||
226 | case 0x13: // TODO check | ||
227 | result = this_->out & 0xFF; | ||
228 | break; | ||
229 | case 0x14: // TODO check | ||
230 | result = this_->out / 256; | ||
231 | break; | ||
232 | default: | ||
233 | result = 255; | ||
234 | } | ||
235 | //PRT_DEBUG("Y8950Adpcm: read "<<(int)rg<<" "<<(int)result); | ||
236 | return result; | ||
237 | } | ||
238 | |||
239 | int ADPCM_calcSample(struct Y8950Adpcm* this_) | ||
240 | { | ||
241 | // This table values are from ymdelta.c by Tatsuyuki Satoh. | ||
242 | static const int F1[16] = { 1, 3, 5, 7, 9, 11, 13, 15, | ||
243 | -1, -3, -5, -7, -9, -11, -13, -15}; | ||
244 | static const int F2[16] = {57, 57, 57, 57, 77, 102, 128, 153, | ||
245 | 57, 57, 57, 57, 77, 102, 128, 153}; | ||
246 | |||
247 | if (ADPCM_muted(this_)) { | ||
248 | return 0; | ||
249 | } | ||
250 | this_->nowStep += this_->step; | ||
251 | if (this_->nowStep >= MAX_STEP) { | ||
252 | int nowLeveling; | ||
253 | do { | ||
254 | this_->nowStep -= MAX_STEP; | ||
255 | unsigned long val; | ||
256 | if (!(this_->playAddr & 1)) { | ||
257 | // n-th nibble | ||
258 | int tmp = this_->playAddr / 2; | ||
259 | if (this_->romBank || (tmp >= this_->ramSize)) { | ||
260 | this_->reg15 = 0xFF; | ||
261 | } else { | ||
262 | this_->reg15 = this_->ramBank[tmp]; | ||
263 | } | ||
264 | val = this_->reg15 >> 4; | ||
265 | } else { | ||
266 | // (n+1)-th nibble | ||
267 | val = this_->reg15 & 0x0F; | ||
268 | } | ||
269 | int prevOut = this_->out; | ||
270 | this_->out = CLAP(DECODE_MIN, this_->out + (this_->diff * F1[val]) / 8, | ||
271 | DECODE_MAX); | ||
272 | this_->diff = CLAP(DMIN, (this_->diff * F2[val]) / 64, DMAX); | ||
273 | int deltaNext = this_->out - prevOut; | ||
274 | nowLeveling = this_->nextLeveling; | ||
275 | this_->nextLeveling = prevOut + deltaNext / 2; | ||
276 | |||
277 | this_->playAddr++; | ||
278 | if (this_->playAddr > this_->stopAddr) { | ||
279 | if (this_->reg7 & R07_REPEAT) { | ||
280 | restart(this_); | ||
281 | } else { | ||
282 | this_->playing = false; | ||
283 | //y8950.setStatus(Y8950::STATUS_EOS); | ||
284 | } | ||
285 | } | ||
286 | } while (this_->nowStep >= MAX_STEP); | ||
287 | this_->sampleStep = (this_->nextLeveling - nowLeveling) * this_->volumeWStep; | ||
288 | this_->output = nowLeveling * this_->volume; | ||
289 | |||
290 | /* TODO: Used fixed point math here */ | ||
291 | #if !defined(ROCKBOX) | ||
292 | this_->output += (int)(((long long)this_->sampleStep * this_->nowStep) / this_->step); | ||
293 | #endif | ||
294 | } | ||
295 | this_->output += this_->sampleStep; | ||
296 | return this_->output >> 12; | ||
297 | } | ||