New makefile solution: A single invocation of 'make' to build the entire tree. Fully...
[kugel-rb.git] / apps / codecs / libasap / apokeysnd.c
blob1d48bc20d27fbd7c6e7086acbed40e608a02e965
1 /*
2 * apokeysnd.c - another POKEY sound emulator
4 * Copyright (C) 2007-2008 Piotr Fusik
6 * This file is part of ASAP (Another Slight Atari Player),
7 * see http://asap.sourceforge.net
9 * ASAP is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2 of the License,
12 * or (at your option) any later version.
14 * ASAP is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty
16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 * See the GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with ASAP; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "codeclib.h"
24 #if !defined(JAVA) && !defined(CSHARP)
25 #include <string.h>
26 #endif
28 #include "asap_internal.h"
30 #define memset ci->memset
31 #define ULTRASOUND_CYCLES 112
33 #define MUTE_FREQUENCY 1
34 #define MUTE_INIT 2
35 #define MUTE_USER 4
37 CONST_LOOKUP(byte, poly4_lookup) =
38 { 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1 };
39 CONST_LOOKUP(byte, poly5_lookup) =
40 { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1,
41 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1 };
43 FILE_FUNC void init_state(PokeyState PTR pst)
45 PST audctl = 0;
46 PST init = FALSE;
47 PST poly_index = 15 * 31 * 131071;
48 PST div_cycles = 28;
49 PST mute1 = MUTE_FREQUENCY | MUTE_USER;
50 PST mute2 = MUTE_FREQUENCY | MUTE_USER;
51 PST mute3 = MUTE_FREQUENCY | MUTE_USER;
52 PST mute4 = MUTE_FREQUENCY | MUTE_USER;
53 PST audf1 = 0;
54 PST audf2 = 0;
55 PST audf3 = 0;
56 PST audf4 = 0;
57 PST audc1 = 0;
58 PST audc2 = 0;
59 PST audc3 = 0;
60 PST audc4 = 0;
61 PST tick_cycle1 = NEVER;
62 PST tick_cycle2 = NEVER;
63 PST tick_cycle3 = NEVER;
64 PST tick_cycle4 = NEVER;
65 PST period_cycles1 = 28;
66 PST period_cycles2 = 28;
67 PST period_cycles3 = 28;
68 PST period_cycles4 = 28;
69 PST reload_cycles1 = 28;
70 PST reload_cycles3 = 28;
71 PST out1 = 0;
72 PST out2 = 0;
73 PST out3 = 0;
74 PST out4 = 0;
75 PST delta1 = 0;
76 PST delta2 = 0;
77 PST delta3 = 0;
78 PST delta4 = 0;
79 PST skctl = 3;
80 ZERO_ARRAY(PST delta_buffer);
83 ASAP_FUNC void PokeySound_Initialize(ASAP_State PTR ast)
85 int i;
86 int reg;
87 reg = 0x1ff;
88 for (i = 0; i < 511; i++) {
89 reg = ((((reg >> 5) ^ reg) & 1) << 8) + (reg >> 1);
90 AST poly9_lookup[i] = (byte) reg;
92 reg = 0x1ffff;
93 for (i = 0; i < 16385; i++) {
94 reg = ((((reg >> 5) ^ reg) & 0xff) << 9) + (reg >> 8);
95 AST poly17_lookup[i] = (byte) (reg >> 1);
97 AST sample_offset = 0;
98 AST sample_index = 0;
99 AST samples = 0;
100 AST iir_acc_left = 0;
101 AST iir_acc_right = 0;
102 init_state(ADDRESSOF AST base_pokey);
103 init_state(ADDRESSOF AST extra_pokey);
106 #define CYCLE_TO_SAMPLE(cycle) (((cycle) * ASAP_SAMPLE_RATE + AST sample_offset) / ASAP_MAIN_CLOCK)
108 #define DO_TICK(ch) \
109 if (PST init) { \
110 switch (PST audc##ch >> 4) { \
111 case 10: \
112 case 14: \
113 PST out##ch ^= 1; \
114 PST delta_buffer[CYCLE_TO_SAMPLE(cycle)] += PST delta##ch = -PST delta##ch; \
115 break; \
116 default: \
117 break; \
120 else { \
121 int poly = cycle + PST poly_index - (ch - 1); \
122 int newout = PST out##ch; \
123 switch (PST audc##ch >> 4) { \
124 case 0: \
125 if (poly5_lookup[poly % 31] != 0) { \
126 if ((PST audctl & 0x80) != 0) \
127 newout = AST poly9_lookup[poly % 511] & 1; \
128 else { \
129 poly %= 131071; \
130 newout = (AST poly17_lookup[poly >> 3] >> (poly & 7)) & 1; \
133 break; \
134 case 2: \
135 case 6: \
136 newout ^= poly5_lookup[poly % 31]; \
137 break; \
138 case 4: \
139 if (poly5_lookup[poly % 31] != 0) \
140 newout = poly4_lookup[poly % 15]; \
141 break; \
142 case 8: \
143 if ((PST audctl & 0x80) != 0) \
144 newout = AST poly9_lookup[poly % 511] & 1; \
145 else { \
146 poly %= 131071; \
147 newout = (AST poly17_lookup[poly >> 3] >> (poly & 7)) & 1; \
149 break; \
150 case 10: \
151 case 14: \
152 newout ^= 1; \
153 break; \
154 case 12: \
155 newout = poly4_lookup[poly % 15]; \
156 break; \
157 default: \
158 break; \
160 if (newout != PST out##ch) { \
161 PST out##ch = newout; \
162 PST delta_buffer[CYCLE_TO_SAMPLE(cycle)] += PST delta##ch = -PST delta##ch; \
166 FILE_FUNC void generate(ASAP_State PTR ast, PokeyState PTR pst, int current_cycle)
168 for (;;) {
169 int cycle = current_cycle;
170 if (cycle > PST tick_cycle1)
171 cycle = PST tick_cycle1;
172 if (cycle > PST tick_cycle2)
173 cycle = PST tick_cycle2;
174 if (cycle > PST tick_cycle3)
175 cycle = PST tick_cycle3;
176 if (cycle > PST tick_cycle4)
177 cycle = PST tick_cycle4;
178 if (cycle == current_cycle)
179 break;
180 if (cycle == PST tick_cycle3) {
181 PST tick_cycle3 += PST period_cycles3;
182 if ((PST audctl & 4) != 0 && PST delta1 > 0 && PST mute1 == 0)
183 PST delta_buffer[CYCLE_TO_SAMPLE(cycle)] += PST delta1 = -PST delta1;
184 DO_TICK(3);
186 if (cycle == PST tick_cycle4) {
187 PST tick_cycle4 += PST period_cycles4;
188 if ((PST audctl & 8) != 0)
189 PST tick_cycle3 = cycle + PST reload_cycles3;
190 if ((PST audctl & 2) != 0 && PST delta2 > 0 && PST mute2 == 0)
191 PST delta_buffer[CYCLE_TO_SAMPLE(cycle)] += PST delta2 = -PST delta2;
192 DO_TICK(4);
194 if (cycle == PST tick_cycle1) {
195 PST tick_cycle1 += PST period_cycles1;
196 if ((PST skctl & 0x88) == 8)
197 PST tick_cycle2 = cycle + PST period_cycles2;
198 DO_TICK(1);
200 if (cycle == PST tick_cycle2) {
201 PST tick_cycle2 += PST period_cycles2;
202 if ((PST audctl & 0x10) != 0)
203 PST tick_cycle1 = cycle + PST reload_cycles1;
204 else if ((PST skctl & 8) != 0)
205 PST tick_cycle1 = cycle + PST period_cycles1;
206 DO_TICK(2);
211 #define MUTE_CHANNEL(ch, cond, mask) \
212 if (cond) { \
213 PST mute##ch |= mask; \
214 PST tick_cycle##ch = NEVER; \
216 else { \
217 PST mute##ch &= ~mask; \
218 if (PST tick_cycle##ch == NEVER && PST mute##ch == 0) \
219 PST tick_cycle##ch = AST cycle; \
222 #define DO_ULTRASOUND(ch) \
223 MUTE_CHANNEL(ch, PST period_cycles##ch <= ULTRASOUND_CYCLES && (PST audc##ch >> 4 == 10 || PST audc##ch >> 4 == 14), MUTE_FREQUENCY)
225 #define DO_AUDC(ch) \
226 if (data == PST audc##ch) \
227 break; \
228 generate(ast, pst, AST cycle); \
229 PST audc##ch = data; \
230 if ((data & 0x10) != 0) { \
231 data &= 0xf; \
232 if ((PST mute##ch & MUTE_USER) == 0) \
233 PST delta_buffer[CYCLE_TO_SAMPLE(AST cycle)] \
234 += PST delta##ch > 0 ? data - PST delta##ch : data; \
235 PST delta##ch = data; \
237 else { \
238 data &= 0xf; \
239 DO_ULTRASOUND(ch); \
240 if (PST delta##ch > 0) { \
241 if ((PST mute##ch & MUTE_USER) == 0) \
242 PST delta_buffer[CYCLE_TO_SAMPLE(AST cycle)] \
243 += data - PST delta##ch; \
244 PST delta##ch = data; \
246 else \
247 PST delta##ch = -data; \
249 break;
251 #define DO_INIT(ch, cond) \
252 MUTE_CHANNEL(ch, PST init && cond, MUTE_INIT)
254 ASAP_FUNC void PokeySound_PutByte(ASAP_State PTR ast, int addr, int data)
256 PokeyState PTR pst = (addr & AST extra_pokey_mask) != 0
257 ? ADDRESSOF AST extra_pokey : ADDRESSOF AST base_pokey;
258 switch (addr & 0xf) {
259 case 0x00:
260 if (data == PST audf1)
261 break;
262 generate(ast, pst, AST cycle);
263 PST audf1 = data;
264 switch (PST audctl & 0x50) {
265 case 0x00:
266 PST period_cycles1 = PST div_cycles * (data + 1);
267 break;
268 case 0x10:
269 PST period_cycles2 = PST div_cycles * (data + 256 * PST audf2 + 1);
270 PST reload_cycles1 = PST div_cycles * (data + 1);
271 DO_ULTRASOUND(2);
272 break;
273 case 0x40:
274 PST period_cycles1 = data + 4;
275 break;
276 case 0x50:
277 PST period_cycles2 = data + 256 * PST audf2 + 7;
278 PST reload_cycles1 = data + 4;
279 DO_ULTRASOUND(2);
280 break;
282 DO_ULTRASOUND(1);
283 break;
284 case 0x01:
285 DO_AUDC(1)
286 case 0x02:
287 if (data == PST audf2)
288 break;
289 generate(ast, pst, AST cycle);
290 PST audf2 = data;
291 switch (PST audctl & 0x50) {
292 case 0x00:
293 case 0x40:
294 PST period_cycles2 = PST div_cycles * (data + 1);
295 break;
296 case 0x10:
297 PST period_cycles2 = PST div_cycles * (PST audf1 + 256 * data + 1);
298 break;
299 case 0x50:
300 PST period_cycles2 = PST audf1 + 256 * data + 7;
301 break;
303 DO_ULTRASOUND(2);
304 break;
305 case 0x03:
306 DO_AUDC(2)
307 case 0x04:
308 if (data == PST audf3)
309 break;
310 generate(ast, pst, AST cycle);
311 PST audf3 = data;
312 switch (PST audctl & 0x28) {
313 case 0x00:
314 PST period_cycles3 = PST div_cycles * (data + 1);
315 break;
316 case 0x08:
317 PST period_cycles4 = PST div_cycles * (data + 256 * PST audf4 + 1);
318 PST reload_cycles3 = PST div_cycles * (data + 1);
319 DO_ULTRASOUND(4);
320 break;
321 case 0x20:
322 PST period_cycles3 = data + 4;
323 break;
324 case 0x28:
325 PST period_cycles4 = data + 256 * PST audf4 + 7;
326 PST reload_cycles3 = data + 4;
327 DO_ULTRASOUND(4);
328 break;
330 DO_ULTRASOUND(3);
331 break;
332 case 0x05:
333 DO_AUDC(3)
334 case 0x06:
335 if (data == PST audf4)
336 break;
337 generate(ast, pst, AST cycle);
338 PST audf4 = data;
339 switch (PST audctl & 0x28) {
340 case 0x00:
341 case 0x20:
342 PST period_cycles4 = PST div_cycles * (data + 1);
343 break;
344 case 0x08:
345 PST period_cycles4 = PST div_cycles * (PST audf3 + 256 * data + 1);
346 break;
347 case 0x28:
348 PST period_cycles4 = PST audf3 + 256 * data + 7;
349 break;
351 DO_ULTRASOUND(4);
352 break;
353 case 0x07:
354 DO_AUDC(4)
355 case 0x08:
356 if (data == PST audctl)
357 break;
358 generate(ast, pst, AST cycle);
359 PST audctl = data;
360 PST div_cycles = ((data & 1) != 0) ? 114 : 28;
361 /* TODO: tick_cycles */
362 switch (data & 0x50) {
363 case 0x00:
364 PST period_cycles1 = PST div_cycles * (PST audf1 + 1);
365 PST period_cycles2 = PST div_cycles * (PST audf2 + 1);
366 break;
367 case 0x10:
368 PST period_cycles1 = PST div_cycles * 256;
369 PST period_cycles2 = PST div_cycles * (PST audf1 + 256 * PST audf2 + 1);
370 PST reload_cycles1 = PST div_cycles * (PST audf1 + 1);
371 break;
372 case 0x40:
373 PST period_cycles1 = PST audf1 + 4;
374 PST period_cycles2 = PST div_cycles * (PST audf2 + 1);
375 break;
376 case 0x50:
377 PST period_cycles1 = 256;
378 PST period_cycles2 = PST audf1 + 256 * PST audf2 + 7;
379 PST reload_cycles1 = PST audf1 + 4;
380 break;
382 DO_ULTRASOUND(1);
383 DO_ULTRASOUND(2);
384 switch (data & 0x28) {
385 case 0x00:
386 PST period_cycles3 = PST div_cycles * (PST audf3 + 1);
387 PST period_cycles4 = PST div_cycles * (PST audf4 + 1);
388 break;
389 case 0x08:
390 PST period_cycles3 = PST div_cycles * 256;
391 PST period_cycles4 = PST div_cycles * (PST audf3 + 256 * PST audf4 + 1);
392 PST reload_cycles3 = PST div_cycles * (PST audf3 + 1);
393 break;
394 case 0x20:
395 PST period_cycles3 = PST audf3 + 4;
396 PST period_cycles4 = PST div_cycles * (PST audf4 + 1);
397 break;
398 case 0x28:
399 PST period_cycles3 = 256;
400 PST period_cycles4 = PST audf3 + 256 * PST audf4 + 7;
401 PST reload_cycles3 = PST audf3 + 4;
402 break;
404 DO_ULTRASOUND(3);
405 DO_ULTRASOUND(4);
406 break;
407 case 0x09:
408 /* TODO: STIMER */
409 break;
410 case 0x0f:
411 PST skctl = data;
412 PST init = ((data & 3) == 0);
413 DO_INIT(1, (PST audctl & 0x40) == 0);
414 DO_INIT(2, (PST audctl & 0x50) != 0x50);
415 DO_INIT(3, (PST audctl & 0x20) == 0);
416 DO_INIT(4, (PST audctl & 0x28) != 0x28);
417 break;
418 default:
419 break;
423 ASAP_FUNC int PokeySound_GetRandom(ASAP_State PTR ast, int addr)
425 PokeyState PTR pst = (addr & AST extra_pokey_mask) != 0
426 ? ADDRESSOF AST extra_pokey : ADDRESSOF AST base_pokey;
427 int i;
428 if (PST init)
429 return 0xff;
430 i = AST cycle + PST poly_index;
431 if ((PST audctl & 0x80) != 0)
432 return AST poly9_lookup[i % 511];
433 else {
434 int j;
435 i %= 131071;
436 j = i >> 3;
437 i &= 7;
438 return ((AST poly17_lookup[j] >> i) + (AST poly17_lookup[j + 1] << (8 - i))) & 0xff;
442 FILE_FUNC void end_frame(ASAP_State PTR ast, PokeyState PTR pst, int cycle_limit)
444 int m;
445 generate(ast, pst, cycle_limit);
446 PST poly_index += cycle_limit;
447 m = ((PST audctl & 0x80) != 0) ? 15 * 31 * 511 : 15 * 31 * 131071;
448 if (PST poly_index >= 2 * m)
449 PST poly_index -= m;
450 if (PST tick_cycle1 != NEVER)
451 PST tick_cycle1 -= cycle_limit;
452 if (PST tick_cycle2 != NEVER)
453 PST tick_cycle2 -= cycle_limit;
454 if (PST tick_cycle3 != NEVER)
455 PST tick_cycle3 -= cycle_limit;
456 if (PST tick_cycle4 != NEVER)
457 PST tick_cycle4 -= cycle_limit;
460 ASAP_FUNC void PokeySound_StartFrame(ASAP_State PTR ast)
462 ZERO_ARRAY(AST base_pokey.delta_buffer);
463 if (AST extra_pokey_mask != 0)
464 ZERO_ARRAY(AST extra_pokey.delta_buffer);
467 ASAP_FUNC void PokeySound_EndFrame(ASAP_State PTR ast, int current_cycle)
469 end_frame(ast, ADDRESSOF AST base_pokey, current_cycle);
470 if (AST extra_pokey_mask != 0)
471 end_frame(ast, ADDRESSOF AST extra_pokey, current_cycle);
472 AST sample_offset += current_cycle * ASAP_SAMPLE_RATE;
473 AST sample_index = 0;
474 AST samples = AST sample_offset / ASAP_MAIN_CLOCK;
475 AST sample_offset %= ASAP_MAIN_CLOCK;
478 ASAP_FUNC int PokeySound_Generate(ASAP_State PTR ast, byte ARRAY buffer, int buffer_offset, int blocks, ASAP_SampleFormat format)
480 int i = AST sample_index;
481 int samples = AST samples;
482 int acc_left = AST iir_acc_left;
483 int acc_right = AST iir_acc_right;
484 if (blocks < samples - i)
485 samples = i + blocks;
486 else
487 blocks = samples - i;
488 for (; i < samples; i++) {
489 int sample;
490 acc_left += (AST base_pokey.delta_buffer[i] << 20) - (acc_left * 3 >> 10);
491 sample = acc_left >> 10;
492 #define STORE_SAMPLE \
493 if (sample < -32767) \
494 sample = -32767; \
495 else if (sample > 32767) \
496 sample = 32767; \
497 switch (format) { \
498 case ASAP_FORMAT_U8: \
499 buffer[buffer_offset++] = (byte) ((sample >> 8) + 128); \
500 break; \
501 case ASAP_FORMAT_S16_LE: \
502 buffer[buffer_offset++] = (byte) sample; \
503 buffer[buffer_offset++] = (byte) (sample >> 8); \
504 break; \
505 case ASAP_FORMAT_S16_BE: \
506 buffer[buffer_offset++] = (byte) (sample >> 8); \
507 buffer[buffer_offset++] = (byte) sample; \
508 break; \
510 STORE_SAMPLE;
511 if (AST extra_pokey_mask != 0) {
512 acc_right += (AST extra_pokey.delta_buffer[i] << 20) - (acc_right * 3 >> 10);
513 sample = acc_right >> 10;
514 STORE_SAMPLE;
517 if (i == AST samples) {
518 acc_left += AST base_pokey.delta_buffer[i] << 20;
519 acc_right += AST extra_pokey.delta_buffer[i] << 20;
521 AST sample_index = i;
522 AST iir_acc_left = acc_left;
523 AST iir_acc_right = acc_right;
524 return blocks;
527 ASAP_FUNC abool PokeySound_IsSilent(const PokeyState PTR pst)
529 return ((PST audc1 | PST audc2 | PST audc3 | PST audc4) & 0xf) == 0;
532 ASAP_FUNC void PokeySound_Mute(const ASAP_State PTR ast, PokeyState PTR pst, int mask)
534 MUTE_CHANNEL(1, (mask & 1) != 0, MUTE_USER);
535 MUTE_CHANNEL(2, (mask & 2) != 0, MUTE_USER);
536 MUTE_CHANNEL(3, (mask & 4) != 0, MUTE_USER);
537 MUTE_CHANNEL(4, (mask & 8) != 0, MUTE_USER);