summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/hes_apu_adpcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/hes_apu_adpcm.c')
-rw-r--r--lib/rbcodec/codecs/libgme/hes_apu_adpcm.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/hes_apu_adpcm.c b/lib/rbcodec/codecs/libgme/hes_apu_adpcm.c
new file mode 100644
index 0000000000..de9b894f5d
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/hes_apu_adpcm.c
@@ -0,0 +1,297 @@
1// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
2
3#include "hes_apu_adpcm.h"
4
5/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
6can redistribute it and/or modify it under the terms of the GNU Lesser
7General Public License as published by the Free Software Foundation; either
8version 2.1 of the License, or (at your option) any later version. This
9module is distributed in the hope that it will be useful, but WITHOUT ANY
10WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12details. You should have received a copy of the GNU Lesser General Public
13License along with this module; if not, write to the Free Software Foundation,
14Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
15
16
17void Adpcm_init( struct Hes_Apu_Adpcm* this )
18{
19 this->output = NULL;
20 memset( &this->state, 0, sizeof( this->state ) );
21 Adpcm_reset( this );
22}
23
24void Adpcm_reset( struct Hes_Apu_Adpcm* this )
25{
26 this->last_time = 0;
27 this->next_timer = 0;
28 this->last_amp = 0;
29
30 memset( &this->state.pcmbuf, 0, sizeof(this->state.pcmbuf) );
31 memset( &this->state.port, 0, sizeof(this->state.port) );
32
33 this->state.ad_sample = 0;
34 this->state.ad_ref_index = 0;
35
36 this->state.addr = 0;
37 this->state.freq = 0;
38 this->state.writeptr = 0;
39 this->state.readptr = 0;
40 this->state.playflag = 0;
41 this->state.repeatflag = 0;
42 this->state.length = 0;
43 this->state.volume = 0xFF;
44 this->state.fadetimer = 0;
45 this->state.fadecount = 0;
46}
47
48static short stepsize[49] = {
49 16, 17, 19, 21, 23, 25, 28,
50 31, 34, 37, 41, 45, 50, 55,
51 60, 66, 73, 80, 88, 97, 107,
52 118, 130, 143, 157, 173, 190, 209,
53 230, 253, 279, 307, 337, 371, 408,
54 449, 494, 544, 598, 658, 724, 796,
55 876, 963,1060,1166,1282,1411,1552
56};
57
58static int Adpcm_decode( struct Hes_Apu_Adpcm* this,int code );
59static int Adpcm_decode( struct Hes_Apu_Adpcm* this,int code )
60{
61 struct State* state = &this->state;
62 int step = stepsize[state->ad_ref_index];
63 int delta;
64 int c = code & 7;
65#if 1
66 delta = 0;
67 if ( c & 4 ) delta += step;
68 step >>= 1;
69 if ( c & 2 ) delta += step;
70 step >>= 1;
71 if ( c & 1 ) delta += step;
72 step >>= 1;
73 delta += step;
74#else
75 delta = ( ( c + c + 1 ) * step ) / 8; // maybe faster, but introduces rounding
76#endif
77 if ( c != code )
78 {
79 state->ad_sample -= delta;
80 if ( state->ad_sample < -2048 )
81 state->ad_sample = -2048;
82 }
83 else
84 {
85 state->ad_sample += delta;
86 if ( state->ad_sample > 2047 )
87 state->ad_sample = 2047;
88 }
89
90 static int const steps [8] = {
91 -1, -1, -1, -1, 2, 4, 6, 8
92 };
93 state->ad_ref_index += steps [c];
94 if ( state->ad_ref_index < 0 )
95 state->ad_ref_index = 0;
96 else if ( state->ad_ref_index > 48 )
97 state->ad_ref_index = 48;
98
99 return state->ad_sample;
100}
101
102static void Adpcm_run_until( struct Hes_Apu_Adpcm* this, blip_time_t end_time );
103static void Adpcm_run_until( struct Hes_Apu_Adpcm* this, blip_time_t end_time )
104{
105 struct State* state = &this->state;
106 int volume = state->volume;
107 int fadetimer = state->fadetimer;
108 int fadecount = state->fadecount;
109 int last_time = this->last_time;
110 int next_timer = this->next_timer;
111 int last_amp = this->last_amp;
112
113 struct Blip_Buffer* output = this->output; // cache often-used values
114
115 while ( state->playflag && last_time < end_time )
116 {
117 while ( last_time >= next_timer )
118 {
119 if ( fadetimer )
120 {
121 if ( fadecount > 0 )
122 {
123 fadecount--;
124 volume = 0xFF * fadecount / fadetimer;
125 }
126 else if ( fadecount < 0 )
127 {
128 fadecount++;
129 volume = 0xFF - ( 0xFF * fadecount / fadetimer );
130 }
131 }
132 next_timer += 7159; // 7159091/1000;
133 }
134 int amp;
135 if ( state->ad_low_nibble )
136 {
137 amp = Adpcm_decode( this, state->pcmbuf[ state->playptr ] & 0x0F );
138 state->ad_low_nibble = false;
139 state->playptr++;
140 state->playedsamplecount++;
141 if ( state->playedsamplecount == state->playlength )
142 {
143 state->playflag = 0;
144 }
145 }
146 else
147 {
148 amp = Adpcm_decode( this, state->pcmbuf[ state->playptr ] >> 4 );
149 state->ad_low_nibble = true;
150 }
151 amp = amp * volume / 0xFF;
152 int delta = amp - last_amp;
153 if ( output && delta )
154 {
155 last_amp = amp;
156 Synth_offset_inline( &this->synth, last_time, delta, output );
157 }
158 last_time += state->freq;
159 }
160
161 if ( !state->playflag )
162 {
163 while ( next_timer <= end_time ) next_timer += 7159; // 7159091/1000
164 last_time = end_time;
165 }
166
167 this->last_time = last_time;
168 this->next_timer = next_timer;
169 this->last_amp = last_amp;
170 state->volume = volume;
171 state->fadetimer = fadetimer;
172 state->fadecount = fadecount;
173}
174
175void Adpcm_write_data( struct Hes_Apu_Adpcm* this, blip_time_t time, int addr, int data )
176{
177 if ( time > this->last_time ) Adpcm_run_until( this, time );
178 struct State* state = &this->state;
179
180 data &= 0xFF;
181 state->port[ addr & 15 ] = data;
182 switch ( addr & 15 )
183 {
184 case 8:
185 state->addr &= 0xFF00;
186 state->addr |= data;
187 break;
188 case 9:
189 state->addr &= 0xFF;
190 state->addr |= data << 8;
191 break;
192 case 10:
193 state->pcmbuf[ state->writeptr++ ] = data;
194 state->playlength ++;
195 break;
196 case 11:
197 dprintf("ADPCM DMA 0x%02X", data);
198 break;
199 case 13:
200 if ( data & 0x80 )
201 {
202 state->addr = 0;
203 state->freq = 0;
204 state->writeptr = 0;
205 state->readptr = 0;
206 state->playflag = 0;
207 state->repeatflag = 0;
208 state->length = 0;
209 state->volume = 0xFF;
210 }
211 if ( ( data & 3 ) == 3 )
212 {
213 state->writeptr = state->addr;
214 }
215 if ( data & 8 )
216 {
217 state->readptr = state->addr ? state->addr - 1 : state->addr;
218 }
219 if ( data & 0x10 )
220 {
221 state->length = state->addr;
222 }
223 state->repeatflag = data & 0x20;
224 state->playflag = data & 0x40;
225 if ( state->playflag )
226 {
227 state->playptr = state->readptr;
228 state->playlength = state->length + 1;
229 state->playedsamplecount = 0;
230 state->ad_sample = 0;
231 state->ad_low_nibble = false;
232 }
233 break;
234 case 14:
235 state->freq = 7159091 / ( 32000 / ( 16 - ( data & 15 ) ) );
236 break;
237 case 15:
238 switch ( data & 15 )
239 {
240 case 0:
241 case 8:
242 case 12:
243 state->fadetimer = -100;
244 state->fadecount = state->fadetimer;
245 break;
246 case 10:
247 state->fadetimer = 5000;
248 state->fadecount = state->fadetimer;
249 break;
250 case 14:
251 state->fadetimer = 1500;
252 state->fadecount = state->fadetimer;
253 break;
254 }
255 break;
256 }
257}
258
259int Adpcm_read_data( struct Hes_Apu_Adpcm* this, blip_time_t time, int addr )
260{
261 if ( time > this->last_time ) Adpcm_run_until( this, time );
262
263 struct State* state = &this->state;
264 switch ( addr & 15 )
265 {
266 case 10:
267 return state->pcmbuf [state->readptr++];
268 case 11:
269 return state->port [11] & ~1;
270 case 12:
271 if (!state->playflag)
272 {
273 state->port [12] |= 1;
274 state->port [12] &= ~8;
275 }
276 else
277 {
278 state->port [12] &= ~1;
279 state->port [12] |= 8;
280 }
281 return state->port [12];
282 case 13:
283 return state->port [13];
284 }
285
286 return 0xFF;
287}
288
289void Adpcm_end_frame( struct Hes_Apu_Adpcm* this, blip_time_t end_time )
290{
291 Adpcm_run_until( this, end_time );
292 this->last_time -= end_time;
293 this->next_timer -= end_time;
294 check( last_time >= 0 );
295 if ( this->output )
296 Blip_set_modified( this->output );
297}