summaryrefslogtreecommitdiff
path: root/apps/codecs/libgme/emuadpcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codecs/libgme/emuadpcm.c')
-rw-r--r--apps/codecs/libgme/emuadpcm.c298
1 files changed, 298 insertions, 0 deletions
diff --git a/apps/codecs/libgme/emuadpcm.c b/apps/codecs/libgme/emuadpcm.c
new file mode 100644
index 0000000000..1f2d5aefa5
--- /dev/null
+++ b/apps/codecs/libgme/emuadpcm.c
@@ -0,0 +1,298 @@
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
57int 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)((double)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)((double)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)((double)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 double factor = (double)this_->volume / (double)oldVol;
192 this_->output = (int)((double)this_->output * factor);
193 this_->sampleStep = (int)((double)this_->sampleStep * factor);
194 }
195 this_->volumeWStep = (int)((double)this_->volume * this_->step / MAX_STEP);
196 break;
197 }
198 case 0x0D: // PRESCALE (L)
199 case 0x0E: // PRESCALE (H)
200 case 0x15: // DAC-DATA (bit9-2)
201 case 0x16: // (bit1-0)
202 case 0x17: // (exponent)
203 case 0x1A: // PCM-DATA
204 // not implemented
205 break;
206 }
207}
208
209byte ADPCM_readReg(struct Y8950Adpcm* this_, byte rg)
210{
211 byte result;
212 switch (rg) {
213 case 0x0F: { // ADPCM-DATA
214 // TODO don't advance pointer when playing???
215 int adr = ((this_->startAddr + this_->memPntr) & this_->addrMask) / 2;
216 if (this_->romBank || (adr >= this_->ramSize)) {
217 result = 0xFF;
218 } else {
219 result = this_->ramBank[adr];
220 }
221 this_->memPntr += 2;
222 if ((this_->startAddr + this_->memPntr) > this_->stopAddr) {
223 OPL_setStatus(this_->y8950, STATUS_EOS);
224 }
225 break;
226 }
227 case 0x13: // TODO check
228 result = this_->out & 0xFF;
229 break;
230 case 0x14: // TODO check
231 result = this_->out / 256;
232 break;
233 default:
234 result = 255;
235 }
236 //PRT_DEBUG("Y8950Adpcm: read "<<(int)rg<<" "<<(int)result);
237 return result;
238}
239
240int ADPCM_calcSample(struct Y8950Adpcm* this_)
241{
242 // This table values are from ymdelta.c by Tatsuyuki Satoh.
243 static const int F1[16] = { 1, 3, 5, 7, 9, 11, 13, 15,
244 -1, -3, -5, -7, -9, -11, -13, -15};
245 static const int F2[16] = {57, 57, 57, 57, 77, 102, 128, 153,
246 57, 57, 57, 57, 77, 102, 128, 153};
247
248 if (ADPCM_muted(this_)) {
249 return 0;
250 }
251 this_->nowStep += this_->step;
252 if (this_->nowStep >= MAX_STEP) {
253 int nowLeveling;
254 do {
255 this_->nowStep -= MAX_STEP;
256 unsigned long val;
257 if (!(this_->playAddr & 1)) {
258 // n-th nibble
259 int tmp = this_->playAddr / 2;
260 if (this_->romBank || (tmp >= this_->ramSize)) {
261 this_->reg15 = 0xFF;
262 } else {
263 this_->reg15 = this_->ramBank[tmp];
264 }
265 val = this_->reg15 >> 4;
266 } else {
267 // (n+1)-th nibble
268 val = this_->reg15 & 0x0F;
269 }
270 int prevOut = this_->out;
271 this_->out = CLAP(DECODE_MIN, this_->out + (this_->diff * F1[val]) / 8,
272 DECODE_MAX);
273 this_->diff = CLAP(DMIN, (this_->diff * F2[val]) / 64, DMAX);
274 int deltaNext = this_->out - prevOut;
275 nowLeveling = this_->nextLeveling;
276 this_->nextLeveling = prevOut + deltaNext / 2;
277
278 this_->playAddr++;
279 if (this_->playAddr > this_->stopAddr) {
280 if (this_->reg7 & R07_REPEAT) {
281 restart(this_);
282 } else {
283 this_->playing = false;
284 //y8950.setStatus(Y8950::STATUS_EOS);
285 }
286 }
287 } while (this_->nowStep >= MAX_STEP);
288 this_->sampleStep = (this_->nextLeveling - nowLeveling) * this_->volumeWStep;
289 this_->output = nowLeveling * this_->volume;
290
291 /* TODO: Used fixed point math here */
292 #if !defined(ROCKBOX)
293 this_->output += (int)((double)this_->sampleStep * ((double)this_->nowStep/(double)this_->step));
294 #endif
295 }
296 this_->output += this_->sampleStep;
297 return this_->output >> 12;
298}