diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-25 21:32:25 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-04-25 22:13:20 +0200 |
commit | f40bfc9267b13b54e6379dfe7539447662879d24 (patch) | |
tree | 9b20069d5e62809ff434061ad730096836f916f2 /lib/rbcodec/codecs/libspc/spc_codec.h | |
parent | a0009907de7a0107d49040d8a180f140e2eff299 (diff) | |
download | rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.tar.gz rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.zip |
Add codecs to librbcodec.
Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97
Reviewed-on: http://gerrit.rockbox.org/137
Reviewed-by: Nils Wallménius <nils@rockbox.org>
Tested-by: Nils Wallménius <nils@rockbox.org>
Diffstat (limited to 'lib/rbcodec/codecs/libspc/spc_codec.h')
-rw-r--r-- | lib/rbcodec/codecs/libspc/spc_codec.h | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libspc/spc_codec.h b/lib/rbcodec/codecs/libspc/spc_codec.h new file mode 100644 index 0000000000..7f6b6e2e9f --- /dev/null +++ b/lib/rbcodec/codecs/libspc/spc_codec.h | |||
@@ -0,0 +1,491 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007-2008 Michael Sevakis (jhMikeS) | ||
11 | * Copyright (C) 2006-2007 Adam Gashlin (hcs) | ||
12 | * Copyright (C) 2004-2007 Shay Green (blargg) | ||
13 | * Copyright (C) 2002 Brad Martin | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License | ||
17 | * as published by the Free Software Foundation; either version 2 | ||
18 | * of the License, or (at your option) any later version. | ||
19 | * | ||
20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
21 | * KIND, either express or implied. | ||
22 | * | ||
23 | ****************************************************************************/ | ||
24 | |||
25 | /* lovingly ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */ | ||
26 | /* DSP Based on Brad Martin's OpenSPC DSP emulator */ | ||
27 | /* tag reading from sexyspc by John Brawn (John_Brawn@yahoo.com) and others */ | ||
28 | |||
29 | #ifndef _SPC_CODEC_H_ | ||
30 | #define _SPC_CODEC_H_ | ||
31 | |||
32 | /* rather than comment out asserts, just define NDEBUG */ | ||
33 | #ifndef NDEBUG | ||
34 | #define NDEBUG | ||
35 | #endif | ||
36 | #include <assert.h> | ||
37 | |||
38 | /** Basic configuration options **/ | ||
39 | |||
40 | #ifndef ARM_ARCH | ||
41 | #define ARM_ARCH 0 | ||
42 | #endif | ||
43 | |||
44 | #define SPC_DUAL_CORE 1 | ||
45 | |||
46 | #if !defined(SPC_DUAL_CORE) || NUM_CORES == 1 | ||
47 | #undef SPC_DUAL_CORE | ||
48 | #define SPC_DUAL_CORE 0 | ||
49 | #endif | ||
50 | |||
51 | /* Only some targets are fast enough for gaussian and realtime BRR decode */ | ||
52 | #if CONFIG_CPU == S3C2440 || CONFIG_CPU == IMX31L || \ | ||
53 | CONFIG_CPU == AS3525 || CONFIG_CPU == AS3525v2 || \ | ||
54 | defined(CPU_S5L870X) || \ | ||
55 | (CONFIG_PLATFORM & PLATFORM_HOSTED) || MEMORYSIZE <= 2 | ||
56 | /* Don't cache BRR waves */ | ||
57 | #define SPC_BRRCACHE 0 | ||
58 | |||
59 | /* Allow gaussian interpolation */ | ||
60 | #define SPC_NOINTERP 0 | ||
61 | |||
62 | /* Allow echo processing */ | ||
63 | #define SPC_NOECHO 0 | ||
64 | #elif defined(CPU_COLDFIRE) | ||
65 | /* Cache BRR waves */ | ||
66 | #define SPC_BRRCACHE 1 | ||
67 | |||
68 | /* Disable gaussian interpolation */ | ||
69 | #define SPC_NOINTERP 1 | ||
70 | |||
71 | /* Allow echo processing */ | ||
72 | #define SPC_NOECHO 0 | ||
73 | #elif defined (CPU_PP) && SPC_DUAL_CORE | ||
74 | /* Cache BRR waves */ | ||
75 | #define SPC_BRRCACHE 1 | ||
76 | |||
77 | /* Disable gaussian interpolation */ | ||
78 | #define SPC_NOINTERP 1 | ||
79 | |||
80 | /* Allow echo processing */ | ||
81 | #define SPC_NOECHO 0 | ||
82 | #else | ||
83 | /* Cache BRR waves */ | ||
84 | #define SPC_BRRCACHE 1 | ||
85 | |||
86 | /* Disable gaussian interpolation */ | ||
87 | #define SPC_NOINTERP 1 | ||
88 | |||
89 | /* Disable echo processing */ | ||
90 | #define SPC_NOECHO 1 | ||
91 | #endif | ||
92 | |||
93 | #if (CONFIG_CPU == MCF5250) | ||
94 | #define IBSS_ATTR_SPC IBSS_ATTR | ||
95 | #define ICODE_ATTR_SPC ICODE_ATTR | ||
96 | #define ICONST_ATTR_SPC ICONST_ATTR | ||
97 | /* Not enough IRAM available to move further data to it. */ | ||
98 | #define IBSS_ATTR_SPC_LARGE_IRAM | ||
99 | |||
100 | #elif (CONFIG_CPU == PP5020) | ||
101 | /* spc is slower on PP5020 when moving data to IRAM. */ | ||
102 | #define IBSS_ATTR_SPC | ||
103 | #define ICODE_ATTR_SPC | ||
104 | #define ICONST_ATTR_SPC | ||
105 | /* Not enough IRAM available to move further data to it. */ | ||
106 | #define IBSS_ATTR_SPC_LARGE_IRAM | ||
107 | |||
108 | #elif (CONFIG_CPU == PP5022) || (CONFIG_CPU == PP5024) | ||
109 | #define IBSS_ATTR_SPC IBSS_ATTR | ||
110 | #define ICODE_ATTR_SPC ICODE_ATTR | ||
111 | #define ICONST_ATTR_SPC ICONST_ATTR | ||
112 | /* Not enough IRAM available to move further data to it. */ | ||
113 | #define IBSS_ATTR_SPC_LARGE_IRAM | ||
114 | |||
115 | #elif defined(CPU_S5L870X) | ||
116 | #define IBSS_ATTR_SPC IBSS_ATTR | ||
117 | #define ICODE_ATTR_SPC ICODE_ATTR | ||
118 | #define ICONST_ATTR_SPC ICONST_ATTR | ||
119 | /* Very large IRAM. Move even more data to it. */ | ||
120 | #define IBSS_ATTR_SPC_LARGE_IRAM IBSS_ATTR | ||
121 | |||
122 | #else | ||
123 | #define IBSS_ATTR_SPC IBSS_ATTR | ||
124 | #define ICODE_ATTR_SPC ICODE_ATTR | ||
125 | #define ICONST_ATTR_SPC ICONST_ATTR | ||
126 | /* Not enough IRAM available to move further data to it. */ | ||
127 | #define IBSS_ATTR_SPC_LARGE_IRAM | ||
128 | #endif | ||
129 | |||
130 | #if SPC_DUAL_CORE | ||
131 | #undef SHAREDBSS_ATTR | ||
132 | #define SHAREDBSS_ATTR __attribute__ ((section(".ibss"))) | ||
133 | #undef SHAREDDATA_ATTR | ||
134 | #define SHAREDDATA_ATTR __attribute__((section(".idata"))) | ||
135 | #endif | ||
136 | |||
137 | /* Samples per channel per iteration */ | ||
138 | #if defined(CPU_PP) && NUM_CORES == 1 | ||
139 | #define WAV_CHUNK_SIZE 2048 | ||
140 | #else | ||
141 | #define WAV_CHUNK_SIZE 1024 | ||
142 | #endif | ||
143 | |||
144 | /**************** Little-endian handling ****************/ | ||
145 | |||
146 | static inline unsigned get_le16( void const* p ) | ||
147 | { | ||
148 | return ((unsigned char const*) p) [1] * 0x100u + | ||
149 | ((unsigned char const*) p) [0]; | ||
150 | } | ||
151 | |||
152 | static inline int get_le16s( void const* p ) | ||
153 | { | ||
154 | return ((signed char const*) p) [1] * 0x100 + | ||
155 | ((unsigned char const*) p) [0]; | ||
156 | } | ||
157 | |||
158 | static inline void set_le16( void* p, unsigned n ) | ||
159 | { | ||
160 | ((unsigned char*) p) [1] = (unsigned char) (n >> 8); | ||
161 | ((unsigned char*) p) [0] = (unsigned char) n; | ||
162 | } | ||
163 | |||
164 | #define GET_LE16( addr ) get_le16( addr ) | ||
165 | #define GET_LE16A( addr ) get_le16( addr ) | ||
166 | #define SET_LE16( addr, data ) set_le16( addr, data ) | ||
167 | #define INT16A( addr ) (*(uint16_t*) (addr)) | ||
168 | #define INT16SA( addr ) (*(int16_t*) (addr)) | ||
169 | |||
170 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
171 | #define GET_LE16SA( addr ) (*( int16_t*) (addr)) | ||
172 | #define SET_LE16A( addr, data ) (void) (*(uint16_t*) (addr) = (data)) | ||
173 | #else | ||
174 | #define GET_LE16SA( addr ) get_le16s( addr ) | ||
175 | #define SET_LE16A( addr, data ) set_le16 ( addr, data ) | ||
176 | #endif | ||
177 | |||
178 | struct Spc_Emu; | ||
179 | #define THIS struct Spc_Emu* const this | ||
180 | |||
181 | /* The CPU portion (shock!) */ | ||
182 | |||
183 | struct cpu_regs_t | ||
184 | { | ||
185 | long pc; /* more than 16 bits to allow overflow detection */ | ||
186 | uint8_t a; | ||
187 | uint8_t x; | ||
188 | uint8_t y; | ||
189 | uint8_t status; | ||
190 | uint8_t sp; | ||
191 | }; | ||
192 | |||
193 | struct src_dir | ||
194 | { | ||
195 | uint16_t start; | ||
196 | uint16_t loop; | ||
197 | }; | ||
198 | |||
199 | struct cpu_ram_t | ||
200 | { | ||
201 | union { | ||
202 | uint8_t padding1 [0x100]; | ||
203 | uint16_t align; | ||
204 | } padding1 [1]; | ||
205 | union { | ||
206 | uint8_t ram [0x10000]; | ||
207 | struct src_dir sd [0x10000/sizeof(struct src_dir)]; | ||
208 | }; | ||
209 | uint8_t padding2 [0x100]; | ||
210 | }; | ||
211 | |||
212 | #undef RAM | ||
213 | #define RAM ram.ram | ||
214 | extern struct cpu_ram_t ram; | ||
215 | |||
216 | long CPU_run( THIS, long start_time ) ICODE_ATTR_SPC; | ||
217 | void CPU_Init( THIS ); | ||
218 | |||
219 | /* The DSP portion (awe!) */ | ||
220 | enum { VOICE_COUNT = 8 }; | ||
221 | enum { REGISTER_COUNT = 128 }; | ||
222 | |||
223 | struct raw_voice_t | ||
224 | { | ||
225 | int8_t volume [2]; | ||
226 | uint8_t rate [2]; | ||
227 | uint8_t waveform; | ||
228 | uint8_t adsr [2]; /* envelope rates for attack, decay, and sustain */ | ||
229 | uint8_t gain; /* envelope gain (if not using ADSR) */ | ||
230 | int8_t envx; /* current envelope level */ | ||
231 | int8_t outx; /* current sample */ | ||
232 | int8_t unused [6]; | ||
233 | }; | ||
234 | |||
235 | struct globals_t | ||
236 | { | ||
237 | int8_t unused1 [12]; | ||
238 | int8_t volume_0; /* 0C Main Volume Left (-.7) */ | ||
239 | int8_t echo_feedback; /* 0D Echo Feedback (-.7) */ | ||
240 | int8_t unused2 [14]; | ||
241 | int8_t volume_1; /* 1C Main Volume Right (-.7) */ | ||
242 | int8_t unused3 [15]; | ||
243 | int8_t echo_volume_0; /* 2C Echo Volume Left (-.7) */ | ||
244 | uint8_t pitch_mods; /* 2D Pitch Modulation on/off for each voice */ | ||
245 | int8_t unused4 [14]; | ||
246 | int8_t echo_volume_1; /* 3C Echo Volume Right (-.7) */ | ||
247 | uint8_t noise_enables; /* 3D Noise output on/off for each voice */ | ||
248 | int8_t unused5 [14]; | ||
249 | uint8_t key_ons; /* 4C Key On for each voice */ | ||
250 | uint8_t echo_ons; /* 4D Echo on/off for each voice */ | ||
251 | int8_t unused6 [14]; | ||
252 | uint8_t key_offs; /* 5C key off for each voice | ||
253 | (instantiates release mode) */ | ||
254 | uint8_t wave_page; /* 5D source directory (wave table offsets) */ | ||
255 | int8_t unused7 [14]; | ||
256 | uint8_t flags; /* 6C flags and noise freq */ | ||
257 | uint8_t echo_page; /* 6D */ | ||
258 | int8_t unused8 [14]; | ||
259 | uint8_t wave_ended; /* 7C */ | ||
260 | uint8_t echo_delay; /* 7D ms >> 4 */ | ||
261 | char unused9 [2]; | ||
262 | }; | ||
263 | |||
264 | enum state_t | ||
265 | { /* -1, 0, +1 allows more efficient if statements */ | ||
266 | state_decay = -1, | ||
267 | state_sustain = 0, | ||
268 | state_attack = +1, | ||
269 | state_release = 2 | ||
270 | }; | ||
271 | |||
272 | struct cache_entry_t | ||
273 | { | ||
274 | int16_t const* samples; | ||
275 | unsigned end; /* past-the-end position */ | ||
276 | unsigned loop; /* number of samples in loop */ | ||
277 | unsigned start_addr; | ||
278 | }; | ||
279 | |||
280 | enum { BRR_BLOCK_SIZE = 16 }; | ||
281 | enum { BRR_CACHE_SIZE = 0x20000 + 32} ; | ||
282 | |||
283 | struct voice_t | ||
284 | { | ||
285 | #if SPC_BRRCACHE | ||
286 | int16_t const* samples; | ||
287 | long wave_end; | ||
288 | int wave_loop; | ||
289 | #else | ||
290 | int16_t samples [3 + BRR_BLOCK_SIZE + 1]; | ||
291 | int block_header; /* header byte from current block */ | ||
292 | #endif | ||
293 | uint8_t const* addr; | ||
294 | short volume [2]; | ||
295 | long position;/* position in samples buffer, with 12-bit fraction */ | ||
296 | short envx; | ||
297 | short env_mode; | ||
298 | short env_timer; | ||
299 | short key_on_delay; | ||
300 | }; | ||
301 | |||
302 | #if SPC_BRRCACHE | ||
303 | /* a little extra for samples that go past end */ | ||
304 | extern int16_t BRRcache [BRR_CACHE_SIZE]; | ||
305 | #endif | ||
306 | |||
307 | enum { FIR_BUF_HALF = 8 }; | ||
308 | |||
309 | #if defined(CPU_COLDFIRE) | ||
310 | /* global because of the large aligment requirement for hardware masking - | ||
311 | * L-R interleaved 16-bit samples for easy loading and mac.w use. | ||
312 | */ | ||
313 | enum | ||
314 | { | ||
315 | FIR_BUF_CNT = FIR_BUF_HALF, | ||
316 | FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ), | ||
317 | FIR_BUF_ALIGN = FIR_BUF_SIZE * 2, | ||
318 | FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) - 1)) | ||
319 | }; | ||
320 | #elif defined (CPU_ARM) | ||
321 | #if ARM_ARCH >= 6 | ||
322 | enum | ||
323 | { | ||
324 | FIR_BUF_CNT = FIR_BUF_HALF * 2, | ||
325 | FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ), | ||
326 | FIR_BUF_ALIGN = FIR_BUF_SIZE, | ||
327 | FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) - 1)) | ||
328 | }; | ||
329 | #else | ||
330 | enum | ||
331 | { | ||
332 | FIR_BUF_CNT = FIR_BUF_HALF * 2 * 2, | ||
333 | FIR_BUF_SIZE = FIR_BUF_CNT * sizeof ( int32_t ), | ||
334 | FIR_BUF_ALIGN = FIR_BUF_SIZE, | ||
335 | FIR_BUF_MASK = ~((FIR_BUF_ALIGN / 2) | (sizeof ( int32_t ) * 2 - 1)) | ||
336 | }; | ||
337 | #endif /* ARM_ARCH */ | ||
338 | #endif /* CPU_* */ | ||
339 | |||
340 | struct Spc_Dsp | ||
341 | { | ||
342 | union | ||
343 | { | ||
344 | struct raw_voice_t voice [VOICE_COUNT]; | ||
345 | uint8_t reg [REGISTER_COUNT]; | ||
346 | struct globals_t g; | ||
347 | int16_t align; | ||
348 | } r; | ||
349 | |||
350 | unsigned echo_pos; | ||
351 | int keys_down; | ||
352 | int noise_count; | ||
353 | uint16_t noise; /* also read as int16_t */ | ||
354 | |||
355 | #if defined(CPU_COLDFIRE) | ||
356 | /* FIR history is interleaved. Hardware handles wrapping by mask. | ||
357 | * |LR|LR|LR|LR|LR|LR|LR|LR| */ | ||
358 | int32_t *fir_ptr; | ||
359 | /* wrapped address just behind current position - | ||
360 | allows mac.w to increment and mask fir_ptr */ | ||
361 | int32_t *last_fir_ptr; | ||
362 | /* copy of echo FIR constants as int16_t for use with mac.w */ | ||
363 | int16_t fir_coeff [VOICE_COUNT]; | ||
364 | #elif defined (CPU_ARM) | ||
365 | /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */ | ||
366 | int32_t *fir_ptr; | ||
367 | #if ARM_ARCH >= 6 | ||
368 | /* FIR history is interleaved with guard to eliminate wrap checking | ||
369 | * when convolving. | ||
370 | * |LR|LR|LR|LR|LR|LR|LR|LR|--|--|--|--|--|--|--|--| */ | ||
371 | /* copy of echo FIR constants as int16_t, loaded as int32 for | ||
372 | * halfword, packed multiples */ | ||
373 | int16_t fir_coeff [VOICE_COUNT]; | ||
374 | #else | ||
375 | /* FIR history is interleaved with guard to eliminate wrap checking | ||
376 | * when convolving. | ||
377 | * |LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|LL|RR|... | ||
378 | * |--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--| */ | ||
379 | /* copy of echo FIR constants as int32_t, for faster access */ | ||
380 | int32_t fir_coeff [VOICE_COUNT]; | ||
381 | #endif /* ARM_ARCH */ | ||
382 | #else /* Unoptimized CPU */ | ||
383 | /* fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code */ | ||
384 | int fir_pos; /* (0 to 7) */ | ||
385 | int fir_buf [FIR_BUF_HALF * 2] [2]; | ||
386 | /* copy of echo FIR constants as int, for faster access */ | ||
387 | int fir_coeff [VOICE_COUNT]; | ||
388 | #endif | ||
389 | |||
390 | struct voice_t voice_state [VOICE_COUNT]; | ||
391 | |||
392 | #if SPC_BRRCACHE | ||
393 | uint8_t oldsize; | ||
394 | struct cache_entry_t wave_entry [256]; | ||
395 | struct cache_entry_t wave_entry_old [256]; | ||
396 | #endif | ||
397 | }; | ||
398 | |||
399 | void DSP_run_( struct Spc_Dsp* this, long count, int32_t* out_buf ) ICODE_ATTR_SPC; | ||
400 | void DSP_reset( struct Spc_Dsp* this ); | ||
401 | |||
402 | static inline void DSP_run( struct Spc_Dsp* this, long count, int32_t* out ) | ||
403 | { | ||
404 | /* Should we just fill the buffer with silence? Flags won't be cleared */ | ||
405 | /* during this run so it seems it should keep resetting every sample. */ | ||
406 | if ( this->r.g.flags & 0x80 ) | ||
407 | DSP_reset( this ); | ||
408 | |||
409 | DSP_run_( this, count, out ); | ||
410 | } | ||
411 | |||
412 | /**************** SPC emulator ****************/ | ||
413 | /* 1.024 MHz clock / 32000 samples per second */ | ||
414 | enum { CLOCKS_PER_SAMPLE = 32 }; | ||
415 | |||
416 | enum { EXTRA_CLOCKS = CLOCKS_PER_SAMPLE / 2 }; | ||
417 | |||
418 | /* using this disables timer (since this will always be in the future) */ | ||
419 | enum { TIMER_DISABLED_TIME = 127 }; | ||
420 | |||
421 | enum { ROM_SIZE = 64 }; | ||
422 | enum { ROM_ADDR = 0xFFC0 }; | ||
423 | |||
424 | enum { TIMER_COUNT = 3 }; | ||
425 | |||
426 | struct Timer | ||
427 | { | ||
428 | long next_tick; | ||
429 | int period; | ||
430 | int count; | ||
431 | int shift; | ||
432 | int enabled; | ||
433 | int counter; | ||
434 | }; | ||
435 | |||
436 | struct Spc_Emu | ||
437 | { | ||
438 | uint8_t cycle_table [0x100]; | ||
439 | struct cpu_regs_t r; | ||
440 | |||
441 | int32_t* sample_buf; | ||
442 | long next_dsp; | ||
443 | int rom_enabled; | ||
444 | int extra_cycles; | ||
445 | |||
446 | struct Timer timer [TIMER_COUNT]; | ||
447 | |||
448 | /* large objects at end */ | ||
449 | struct Spc_Dsp dsp; | ||
450 | uint8_t extra_ram [ROM_SIZE]; | ||
451 | uint8_t boot_rom [ROM_SIZE]; | ||
452 | }; | ||
453 | |||
454 | enum { SPC_FILE_SIZE = 0x10180 }; | ||
455 | |||
456 | struct spc_file_t | ||
457 | { | ||
458 | char signature [27]; | ||
459 | char unused [10]; | ||
460 | uint8_t pc [2]; | ||
461 | uint8_t a; | ||
462 | uint8_t x; | ||
463 | uint8_t y; | ||
464 | uint8_t status; | ||
465 | uint8_t sp; | ||
466 | char unused2 [212]; | ||
467 | uint8_t ram [0x10000]; | ||
468 | uint8_t dsp [128]; | ||
469 | uint8_t ipl_rom [128]; | ||
470 | }; | ||
471 | |||
472 | void SPC_Init( THIS ); | ||
473 | |||
474 | int SPC_load_spc( THIS, const void* data, long size ); | ||
475 | |||
476 | /**************** DSP interaction ****************/ | ||
477 | void DSP_write( struct Spc_Dsp* this, int i, int data ) ICODE_ATTR_SPC; | ||
478 | |||
479 | static inline int DSP_read( struct Spc_Dsp* this, int i ) | ||
480 | { | ||
481 | assert( (unsigned) i < REGISTER_COUNT ); | ||
482 | return this->r.reg [i]; | ||
483 | } | ||
484 | |||
485 | int SPC_read( THIS, unsigned addr, long const time ) ICODE_ATTR_SPC; | ||
486 | void SPC_write( THIS, unsigned addr, int data, long const time ) ICODE_ATTR_SPC; | ||
487 | |||
488 | /**************** Sample generation ****************/ | ||
489 | int SPC_play( THIS, long count, int32_t* out ) ICODE_ATTR_SPC; | ||
490 | |||
491 | #endif /* _SPC_CODEC_H_ */ | ||