summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/emuadpcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/emuadpcm.c')
-rw-r--r--lib/rbcodec/codecs/libgme/emuadpcm.c297
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
18const int ADPCM_VOLUME = 356;
19
20// Bitmask for register 0x07
21static const int R07_RESET = 0x01;
22//static const int R07 = 0x02;. // not used
23//static const int R07 = 0x04;. // not used
24const int R07_SP_OFF = 0x08;
25const int R07_REPEAT = 0x10;
26const int R07_MEMORY_DATA = 0x20;
27const int R07_REC = 0x40;
28const int R07_START = 0x80;
29
30//Bitmask for register 0x08
31const int R08_ROM = 0x01;
32const int R08_64K = 0x02;
33const int R08_DA_AD = 0x04;
34const int R08_SAMPL = 0x08;
35//const int R08 = 0x10;. // not used
36//const int R08 = 0x20;. // not used
37const int R08_NOTE_SET = 0x40;
38const int R08_CSM = 0x80;
39
40const int DMAX = 0x6000;
41const int DMIN = 0x7F;
42const int DDEF = 0x7F;
43
44const int DECODE_MAX = 32767;
45const 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
57static 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
69void 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
79void restart(struct Y8950Adpcm* this_);
80void 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
95void ADPCM_setSampleRate(struct Y8950Adpcm* this_, int sr, int clk)
96{
97 this_->sampleRate = sr;
98 this_->clockRate = clk;
99}
100
101bool 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
112void 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
123void 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
208byte 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
239int 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}