2 * apokeysnd.c - another POKEY sound emulator
4 * Copyright (C) 2007-2009 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
24 #include "asap_internal.h"
26 #define ULTRASOUND_CYCLES 112
28 #define MUTE_FREQUENCY 1
32 CONST_ARRAY(byte
, poly4_lookup
)
33 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1
35 CONST_ARRAY(byte
, poly5_lookup
)
36 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1,
37 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1
40 PRIVATE
FUNC(void, PokeySound_InitializeChip
, (P(PokeyState PTR
, pst
)))
44 pst _ poly_index
= 15 * 31 * 131071;
45 pst _ div_cycles
= 28;
46 pst _ mute1
= MUTE_FREQUENCY
| MUTE_USER
;
47 pst _ mute2
= MUTE_FREQUENCY
| MUTE_USER
;
48 pst _ mute3
= MUTE_FREQUENCY
| MUTE_USER
;
49 pst _ mute4
= MUTE_FREQUENCY
| MUTE_USER
;
58 pst _ tick_cycle1
= NEVER
;
59 pst _ tick_cycle2
= NEVER
;
60 pst _ tick_cycle3
= NEVER
;
61 pst _ tick_cycle4
= NEVER
;
62 pst _ period_cycles1
= 28;
63 pst _ period_cycles2
= 28;
64 pst _ period_cycles3
= 28;
65 pst _ period_cycles4
= 28;
66 pst _ reload_cycles1
= 28;
67 pst _ reload_cycles3
= 28;
77 ZERO_ARRAY(pst _ delta_buffer
);
80 FUNC(void, PokeySound_Initialize
, (P(ASAP_State PTR
, ast
)))
85 for (i
= 0; i
< 511; i
++) {
86 reg
= ((((reg
>> 5) ^ reg
) & 1) << 8) + (reg
>> 1);
87 ast _ poly9_lookup
[i
] = TO_BYTE(reg
);
90 for (i
= 0; i
< 16385; i
++) {
91 reg
= ((((reg
>> 5) ^ reg
) & 0xff) << 9) + (reg
>> 8);
92 ast _ poly17_lookup
[i
] = TO_BYTE(reg
>> 1);
94 ast _ sample_offset
= 0;
95 ast _ sample_index
= 0;
97 ast _ iir_acc_left
= 0;
98 ast _ iir_acc_right
= 0;
99 PokeySound_InitializeChip(ADDRESSOF ast _ base_pokey
);
100 PokeySound_InitializeChip(ADDRESSOF ast _ extra_pokey
);
103 #define DO_TICK(ch) \
105 switch (pst _ audc##ch >> 4) { \
108 pst _ out##ch ^= 1; \
109 pst _ delta_buffer[CYCLE_TO_SAMPLE(cycle)] += pst _ delta##ch = -pst _ delta##ch; \
116 V(int, poly) = cycle + pst _ poly_index - (ch - 1); \
117 V(int, newout) = pst _ out##ch; \
118 switch (pst _ audc##ch >> 4) { \
120 if (poly5_lookup[poly % 31] != 0) { \
121 if ((pst _ audctl & 0x80) != 0) \
122 newout = ast _ poly9_lookup[poly % 511] & 1; \
125 newout = (ast _ poly17_lookup[poly >> 3] >> (poly & 7)) & 1; \
131 newout ^= poly5_lookup[poly % 31]; \
134 if (poly5_lookup[poly % 31] != 0) \
135 newout = poly4_lookup[poly % 15]; \
138 if ((pst _ audctl & 0x80) != 0) \
139 newout = ast _ poly9_lookup[poly % 511] & 1; \
142 newout = (ast _ poly17_lookup[poly >> 3] >> (poly & 7)) & 1; \
150 newout = poly4_lookup[poly % 15]; \
155 if (newout != pst _ out##ch) { \
156 pst _ out##ch = newout; \
157 pst _ delta_buffer[CYCLE_TO_SAMPLE(cycle)] += pst _ delta##ch = -pst _ delta##ch; \
161 /* Fills delta_buffer up to current_cycle basing on current AUDF/AUDC/AUDCTL values. */
162 PRIVATE
FUNC(void, PokeySound_GenerateUntilCycle
, (P(ASAP_State PTR
, ast
), P(PokeyState PTR
, pst
), P(int, current_cycle
)))
165 V(int, cycle
) = current_cycle
;
166 if (cycle
> pst _ tick_cycle1
)
167 cycle
= pst _ tick_cycle1
;
168 if (cycle
> pst _ tick_cycle2
)
169 cycle
= pst _ tick_cycle2
;
170 if (cycle
> pst _ tick_cycle3
)
171 cycle
= pst _ tick_cycle3
;
172 if (cycle
> pst _ tick_cycle4
)
173 cycle
= pst _ tick_cycle4
;
174 if (cycle
== current_cycle
)
176 if (cycle
== pst _ tick_cycle3
) {
177 pst _ tick_cycle3
+= pst _ period_cycles3
;
178 if ((pst _ audctl
& 4) != 0 && pst _ delta1
> 0 && pst _ mute1
== 0)
179 pst _ delta_buffer
[CYCLE_TO_SAMPLE(cycle
)] += pst _ delta1
= -pst _ delta1
;
182 if (cycle
== pst _ tick_cycle4
) {
183 pst _ tick_cycle4
+= pst _ period_cycles4
;
184 if ((pst _ audctl
& 8) != 0)
185 pst _ tick_cycle3
= cycle
+ pst _ reload_cycles3
;
186 if ((pst _ audctl
& 2) != 0 && pst _ delta2
> 0 && pst _ mute2
== 0)
187 pst _ delta_buffer
[CYCLE_TO_SAMPLE(cycle
)] += pst _ delta2
= -pst _ delta2
;
190 if (cycle
== pst _ tick_cycle1
) {
191 pst _ tick_cycle1
+= pst _ period_cycles1
;
192 if ((pst _ skctl
& 0x88) == 8) /* two-tone, sending 1 (i.e. timer1) */
193 pst _ tick_cycle2
= cycle
+ pst _ period_cycles2
;
196 if (cycle
== pst _ tick_cycle2
) {
197 pst _ tick_cycle2
+= pst _ period_cycles2
;
198 if ((pst _ audctl
& 0x10) != 0)
199 pst _ tick_cycle1
= cycle
+ pst _ reload_cycles1
;
200 else if ((pst _ skctl
& 8) != 0) /* two-tone */
201 pst _ tick_cycle1
= cycle
+ pst _ period_cycles1
;
209 #define CURRENT_CYCLE 0
210 #define CURRENT_SAMPLE 0
211 #define DO_STORE(reg) \
212 if (data == pst _ reg) \
218 #define CURRENT_CYCLE ast _ cycle
219 #define CURRENT_SAMPLE CYCLE_TO_SAMPLE(ast _ cycle)
220 #define DO_STORE(reg) \
221 if (data == pst _ reg) \
223 PokeySound_GenerateUntilCycle(ast, pst, ast _ cycle); \
226 #endif /* APOKEYSND */
228 #define MUTE_CHANNEL(ch, cond, mask) \
230 pst _ mute##ch |= mask; \
231 pst _ tick_cycle##ch = NEVER; \
234 pst _ mute##ch &= ~mask; \
235 if (pst _ tick_cycle##ch == NEVER && pst _ mute##ch == 0) \
236 pst _ tick_cycle##ch = CURRENT_CYCLE; \
239 #define DO_ULTRASOUND(ch) \
240 MUTE_CHANNEL(ch, pst _ period_cycles##ch <= ULTRASOUND_CYCLES && (pst _ audc##ch >> 4 == 10 || pst _ audc##ch >> 4 == 14), MUTE_FREQUENCY)
242 #define DO_AUDC(ch) \
243 DO_STORE(audc##ch); \
244 if ((data & 0x10) != 0) { \
245 data = (data & 0xf) << DELTA_SHIFT_POKEY; \
246 if ((pst _ mute##ch & MUTE_USER) == 0) \
247 pst _ delta_buffer[CURRENT_SAMPLE] \
248 += pst _ delta##ch > 0 ? data - pst _ delta##ch : data; \
249 pst _ delta##ch = data; \
252 data = (data & 0xf) << DELTA_SHIFT_POKEY; \
254 if (pst _ delta##ch > 0) { \
255 if ((pst _ mute##ch & MUTE_USER) == 0) \
256 pst _ delta_buffer[CURRENT_SAMPLE] \
257 += data - pst _ delta##ch; \
258 pst _ delta##ch = data; \
261 pst _ delta##ch = -data; \
265 #define DO_INIT(ch, cond) \
266 MUTE_CHANNEL(ch, pst _ init && cond, MUTE_INIT)
268 FUNC(void, PokeySound_PutByte
, (P(ASAP_State PTR
, ast
), P(int, addr
), P(int, data
)))
270 V(PokeyState PTR
, pst
) = (addr
& ast _ extra_pokey_mask
) != 0
271 ? ADDRESSOF ast _ extra_pokey
: ADDRESSOF ast _ base_pokey
;
272 switch (addr
& 0xf) {
275 switch (pst _ audctl
& 0x50) {
277 pst _ period_cycles1
= pst _ div_cycles
* (data
+ 1);
280 pst _ period_cycles2
= pst _ div_cycles
* (data
+ 256 * pst _ audf2
+ 1);
281 pst _ reload_cycles1
= pst _ div_cycles
* (data
+ 1);
285 pst _ period_cycles1
= data
+ 4;
288 pst _ period_cycles2
= data
+ 256 * pst _ audf2
+ 7;
289 pst _ reload_cycles1
= data
+ 4;
299 switch (pst _ audctl
& 0x50) {
302 pst _ period_cycles2
= pst _ div_cycles
* (data
+ 1);
305 pst _ period_cycles2
= pst _ div_cycles
* (pst _ audf1
+ 256 * data
+ 1);
308 pst _ period_cycles2
= pst _ audf1
+ 256 * data
+ 7;
317 switch (pst _ audctl
& 0x28) {
319 pst _ period_cycles3
= pst _ div_cycles
* (data
+ 1);
322 pst _ period_cycles4
= pst _ div_cycles
* (data
+ 256 * pst _ audf4
+ 1);
323 pst _ reload_cycles3
= pst _ div_cycles
* (data
+ 1);
327 pst _ period_cycles3
= data
+ 4;
330 pst _ period_cycles4
= data
+ 256 * pst _ audf4
+ 7;
331 pst _ reload_cycles3
= data
+ 4;
341 switch (pst _ audctl
& 0x28) {
344 pst _ period_cycles4
= pst _ div_cycles
* (data
+ 1);
347 pst _ period_cycles4
= pst _ div_cycles
* (pst _ audf3
+ 256 * data
+ 1);
350 pst _ period_cycles4
= pst _ audf3
+ 256 * data
+ 7;
359 pst _ div_cycles
= ((data
& 1) != 0) ? 114 : 28;
360 /* TODO: tick_cycles */
361 switch (data
& 0x50) {
363 pst _ period_cycles1
= pst _ div_cycles
* (pst _ audf1
+ 1);
364 pst _ period_cycles2
= pst _ div_cycles
* (pst _ audf2
+ 1);
367 pst _ period_cycles1
= pst _ div_cycles
* 256;
368 pst _ period_cycles2
= pst _ div_cycles
* (pst _ audf1
+ 256 * pst _ audf2
+ 1);
369 pst _ reload_cycles1
= pst _ div_cycles
* (pst _ audf1
+ 1);
372 pst _ period_cycles1
= pst _ audf1
+ 4;
373 pst _ period_cycles2
= pst _ div_cycles
* (pst _ audf2
+ 1);
376 pst _ period_cycles1
= 256;
377 pst _ period_cycles2
= pst _ audf1
+ 256 * pst _ audf2
+ 7;
378 pst _ reload_cycles1
= pst _ audf1
+ 4;
383 switch (data
& 0x28) {
385 pst _ period_cycles3
= pst _ div_cycles
* (pst _ audf3
+ 1);
386 pst _ period_cycles4
= pst _ div_cycles
* (pst _ audf4
+ 1);
389 pst _ period_cycles3
= pst _ div_cycles
* 256;
390 pst _ period_cycles4
= pst _ div_cycles
* (pst _ audf3
+ 256 * pst _ audf4
+ 1);
391 pst _ reload_cycles3
= pst _ div_cycles
* (pst _ audf3
+ 1);
394 pst _ period_cycles3
= pst _ audf3
+ 4;
395 pst _ period_cycles4
= pst _ div_cycles
* (pst _ audf4
+ 1);
398 pst _ period_cycles3
= 256;
399 pst _ period_cycles4
= pst _ audf3
+ 256 * pst _ audf4
+ 7;
400 pst _ reload_cycles3
= pst _ audf3
+ 4;
405 DO_INIT(1, (data
& 0x40) == 0);
406 DO_INIT(2, (data
& 0x50) != 0x50);
407 DO_INIT(3, (data
& 0x20) == 0);
408 DO_INIT(4, (data
& 0x28) != 0x28);
415 pst _ init
= ((data
& 3) == 0);
416 DO_INIT(1, (pst _ audctl
& 0x40) == 0);
417 DO_INIT(2, (pst _ audctl
& 0x50) != 0x50);
418 DO_INIT(3, (pst _ audctl
& 0x20) == 0);
419 DO_INIT(4, (pst _ audctl
& 0x28) != 0x28);
426 FUNC(int, PokeySound_GetRandom
, (P(ASAP_State PTR
, ast
), P(int, addr
), P(int, cycle
)))
428 V(PokeyState PTR
, pst
) = (addr
& ast _ extra_pokey_mask
) != 0
429 ? ADDRESSOF ast _ extra_pokey
: ADDRESSOF ast _ base_pokey
;
433 i
= cycle
+ pst _ poly_index
;
434 if ((pst _ audctl
& 0x80) != 0)
435 return ast _ poly9_lookup
[i
% 511];
441 return ((ast _ poly17_lookup
[j
] >> i
) + (ast _ poly17_lookup
[j
+ 1] << (8 - i
))) & 0xff;
445 PRIVATE
FUNC(void, end_frame
, (P(ASAP_State PTR
, ast
), P(PokeyState PTR
, pst
), P(int, cycle_limit
)))
448 PokeySound_GenerateUntilCycle(ast
, pst
, cycle_limit
);
449 pst _ poly_index
+= cycle_limit
;
450 m
= ((pst _ audctl
& 0x80) != 0) ? 15 * 31 * 511 : 15 * 31 * 131071;
451 if (pst _ poly_index
>= 2 * m
)
452 pst _ poly_index
-= m
;
453 if (pst _ tick_cycle1
!= NEVER
)
454 pst _ tick_cycle1
-= cycle_limit
;
455 if (pst _ tick_cycle2
!= NEVER
)
456 pst _ tick_cycle2
-= cycle_limit
;
457 if (pst _ tick_cycle3
!= NEVER
)
458 pst _ tick_cycle3
-= cycle_limit
;
459 if (pst _ tick_cycle4
!= NEVER
)
460 pst _ tick_cycle4
-= cycle_limit
;
463 FUNC(void, PokeySound_StartFrame
, (P(ASAP_State PTR
, ast
)))
465 ZERO_ARRAY(ast _ base_pokey
.delta_buffer
);
466 if (ast _ extra_pokey_mask
!= 0)
467 ZERO_ARRAY(ast _ extra_pokey
.delta_buffer
);
470 FUNC(void, PokeySound_EndFrame
, (P(ASAP_State PTR
, ast
), P(int, current_cycle
)))
472 end_frame(ast
, ADDRESSOF ast _ base_pokey
, current_cycle
);
473 if (ast _ extra_pokey_mask
!= 0)
474 end_frame(ast
, ADDRESSOF ast _ extra_pokey
, current_cycle
);
475 ast _ sample_offset
+= current_cycle
* ASAP_SAMPLE_RATE
;
476 ast _ sample_index
= 0;
477 ast _ samples
= TO_INT(ast _ sample_offset
/ ASAP_MAIN_CLOCK
);
478 ast _ sample_offset
%= ASAP_MAIN_CLOCK
;
481 /* Fills buffer with samples from delta_buffer. */
482 FUNC(int, PokeySound_Generate
, (P(ASAP_State PTR
, ast
), P(BYTEARRAY
, buffer
), P(int, buffer_offset
), P(int, blocks
), P(ASAP_SampleFormat
, format
)))
484 V(int, i
) = ast _ sample_index
;
485 V(int, samples
) = ast _ samples
;
486 V(int, acc_left
) = ast _ iir_acc_left
;
487 V(int, acc_right
) = ast _ iir_acc_right
;
488 if (blocks
< samples
- i
)
489 samples
= i
+ blocks
;
491 blocks
= samples
- i
;
492 for (; i
< samples
; i
++) {
494 acc_left
+= ast _ base_pokey
.delta_buffer
[i
] - (acc_left
* 3 >> 10);
495 var sample
: Number
= acc_left
/ 33553408;
496 buffer
.writeFloat(sample
);
497 if (ast
.extra_pokey_mask
!= 0) {
498 acc_right
+= ast _ extra_pokey
.delta_buffer
[i
] - (acc_right
* 3 >> 10);
499 sample
= acc_right
/ 33553408;
501 buffer
.writeFloat(sample
);
504 acc_left
+= ast _ base_pokey
.delta_buffer
[i
] - (acc_left
* 3 >> 10);
505 sample
= acc_left
>> 10;
506 #define STORE_SAMPLE \
507 if (sample < -32767) \
509 else if (sample > 32767) \
512 case ASAP_FORMAT_U8: \
513 buffer[buffer_offset++] = CAST(byte) ((sample >> 8) + 128); \
515 case ASAP_FORMAT_S16_LE: \
516 buffer[buffer_offset++] = TO_BYTE(sample); \
517 buffer[buffer_offset++] = TO_BYTE(sample >> 8); \
519 case ASAP_FORMAT_S16_BE: \
520 buffer[buffer_offset++] = TO_BYTE(sample >> 8); \
521 buffer[buffer_offset++] = TO_BYTE(sample); \
525 if (ast _ extra_pokey_mask
!= 0) {
526 acc_right
+= ast _ extra_pokey
.delta_buffer
[i
] - (acc_right
* 3 >> 10);
527 sample
= acc_right
>> 10;
530 #endif /* ACTIONSCRIPT */
532 if (i
== ast _ samples
) {
533 acc_left
+= ast _ base_pokey
.delta_buffer
[i
];
534 acc_right
+= ast _ extra_pokey
.delta_buffer
[i
];
536 ast _ sample_index
= i
;
537 ast _ iir_acc_left
= acc_left
;
538 ast _ iir_acc_right
= acc_right
;
540 return buffer_offset
;
546 FUNC(abool
, PokeySound_IsSilent
, (P(CONST PokeyState PTR
, pst
)))
548 return ((pst _ audc1
| pst _ audc2
| pst _ audc3
| pst _ audc4
) & 0xf) == 0;
551 FUNC(void, PokeySound_Mute
, (P(CONST ASAP_State PTR
, ast
), P(PokeyState PTR
, pst
), P(int, mask
)))
553 MUTE_CHANNEL(1, (mask
& 1) != 0, MUTE_USER
);
554 MUTE_CHANNEL(2, (mask
& 2) != 0, MUTE_USER
);
555 MUTE_CHANNEL(3, (mask
& 4) != 0, MUTE_USER
);
556 MUTE_CHANNEL(4, (mask
& 8) != 0, MUTE_USER
);
561 static ASAP_State asap
;
563 __declspec(dllexport
) void APokeySound_Initialize(abool stereo
)
565 asap
.extra_pokey_mask
= stereo
? 0x10 : 0;
566 PokeySound_Initialize(&asap
);
567 PokeySound_Mute(&asap
, &asap
.base_pokey
, 0);
568 PokeySound_Mute(&asap
, &asap
.extra_pokey
, 0);
569 PokeySound_StartFrame(&asap
);
572 __declspec(dllexport
) void APokeySound_PutByte(int addr
, int data
)
574 PokeySound_PutByte(&asap
, addr
, data
);
577 __declspec(dllexport
) int APokeySound_GetRandom(int addr
, int cycle
)
579 return PokeySound_GetRandom(&asap
, addr
, cycle
);
582 __declspec(dllexport
) int APokeySound_Generate(int cycles
, byte buffer
[], ASAP_SampleFormat format
)
585 PokeySound_EndFrame(&asap
, cycles
);
586 len
= PokeySound_Generate(&asap
, buffer
, 0, asap
.samples
, format
);
587 PokeySound_StartFrame(&asap
);
591 __declspec(dllexport
) void APokeySound_About(const char **name
, const char **author
, const char **description
)
593 *name
= "Another POKEY sound emulator, v" ASAP_VERSION
;
594 *author
= "Piotr Fusik, (C) " ASAP_YEARS
;
595 *description
= "Part of ASAP, http://asap.sourceforge.net";
598 #endif /* APOKEYSND */