diff options
Diffstat (limited to 'apps/codecs/libgme/kss_scc_apu.c')
-rw-r--r-- | apps/codecs/libgme/kss_scc_apu.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/apps/codecs/libgme/kss_scc_apu.c b/apps/codecs/libgme/kss_scc_apu.c new file mode 100644 index 0000000000..0e71b1ce3b --- /dev/null +++ b/apps/codecs/libgme/kss_scc_apu.c | |||
@@ -0,0 +1,166 @@ | |||
1 | // Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "kss_scc_apu.h" | ||
4 | |||
5 | /* Copyright (C) 2006-2008 Shay Green. This module is free software; you | ||
6 | can redistribute it and/or modify it under the terms of the GNU Lesser | ||
7 | General Public License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. This | ||
9 | module is distributed in the hope that it will be useful, but WITHOUT ANY | ||
10 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
11 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | ||
12 | details. You should have received a copy of the GNU Lesser General Public | ||
13 | License along with this module; if not, write to the Free Software Foundation, | ||
14 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | ||
15 | |||
16 | #include "blargg_source.h" | ||
17 | |||
18 | // Tones above this frequency are treated as disabled tone at half volume. | ||
19 | // Power of two is more efficient (avoids division). | ||
20 | extern int const inaudible_freq; | ||
21 | |||
22 | int const wave_size = 0x20; | ||
23 | |||
24 | static void set_output( struct Scc_Apu* this, struct Blip_Buffer* buf ) | ||
25 | { | ||
26 | int i; | ||
27 | for ( i = 0; i < scc_osc_count; ++i ) | ||
28 | Scc_set_output( this, i, buf ); | ||
29 | } | ||
30 | |||
31 | void Scc_volume( struct Scc_Apu* this, double v ) | ||
32 | { | ||
33 | Synth_volume( &this->synth, 0.43 / scc_osc_count / scc_amp_range * v ); | ||
34 | } | ||
35 | |||
36 | void Scc_reset( struct Scc_Apu* this ) | ||
37 | { | ||
38 | this->last_time = 0; | ||
39 | |||
40 | int i; | ||
41 | for ( i = scc_osc_count; --i >= 0; ) | ||
42 | memset( &this->oscs [i], 0, offsetof (struct scc_osc_t,output) ); | ||
43 | |||
44 | memset( this->regs, 0, sizeof this->regs ); | ||
45 | } | ||
46 | |||
47 | void Scc_init( struct Scc_Apu* this ) | ||
48 | { | ||
49 | Synth_init( &this->synth); | ||
50 | |||
51 | set_output( this, NULL ); | ||
52 | Scc_volume( this, 1.0 ); | ||
53 | Scc_reset( this ); | ||
54 | } | ||
55 | |||
56 | static void run_until( struct Scc_Apu* this, blip_time_t end_time ) | ||
57 | { | ||
58 | int index; | ||
59 | for ( index = 0; index < scc_osc_count; index++ ) | ||
60 | { | ||
61 | struct scc_osc_t* osc = &this->oscs [index]; | ||
62 | |||
63 | struct Blip_Buffer* const output = osc->output; | ||
64 | if ( !output ) | ||
65 | continue; | ||
66 | |||
67 | blip_time_t period = (this->regs [0xA0 + index * 2 + 1] & 0x0F) * 0x100 + | ||
68 | this->regs [0xA0 + index * 2] + 1; | ||
69 | int volume = 0; | ||
70 | if ( this->regs [0xAF] & (1 << index) ) | ||
71 | { | ||
72 | blip_time_t inaudible_period = (unsigned) (Blip_clock_rate( output ) + | ||
73 | inaudible_freq * 32) / (unsigned) (inaudible_freq * 16); | ||
74 | if ( period > inaudible_period ) | ||
75 | volume = (this->regs [0xAA + index] & 0x0F) * (scc_amp_range / 256 / 15); | ||
76 | } | ||
77 | |||
78 | int8_t const* wave = (int8_t*) this->regs + index * wave_size; | ||
79 | /*if ( index == osc_count - 1 ) | ||
80 | wave -= wave_size; // last two oscs share same wave RAM*/ | ||
81 | |||
82 | { | ||
83 | int delta = wave [osc->phase] * volume - osc->last_amp; | ||
84 | if ( delta ) | ||
85 | { | ||
86 | osc->last_amp += delta; | ||
87 | Blip_set_modified( output ); | ||
88 | Synth_offset( &this->synth, this->last_time, delta, output ); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | blip_time_t time = this->last_time + osc->delay; | ||
93 | if ( time < end_time ) | ||
94 | { | ||
95 | int phase = osc->phase; | ||
96 | if ( !volume ) | ||
97 | { | ||
98 | // maintain phase | ||
99 | int count = (end_time - time + period - 1) / period; | ||
100 | phase += count; // will be masked below | ||
101 | time += count * period; | ||
102 | } | ||
103 | else | ||
104 | { | ||
105 | int last_wave = wave [phase]; | ||
106 | phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop | ||
107 | do | ||
108 | { | ||
109 | int delta = wave [phase] - last_wave; | ||
110 | phase = (phase + 1) & (wave_size - 1); | ||
111 | if ( delta ) | ||
112 | { | ||
113 | last_wave += delta; | ||
114 | Synth_offset_inline( &this->synth, time, delta * volume, output ); | ||
115 | } | ||
116 | time += period; | ||
117 | } | ||
118 | while ( time < end_time ); | ||
119 | |||
120 | osc->last_amp = last_wave * volume; | ||
121 | Blip_set_modified( output ); | ||
122 | phase--; // undo pre-advance | ||
123 | } | ||
124 | osc->phase = phase & (wave_size - 1); | ||
125 | } | ||
126 | osc->delay = time - end_time; | ||
127 | } | ||
128 | this->last_time = end_time; | ||
129 | } | ||
130 | |||
131 | void Scc_write( struct Scc_Apu* this, blip_time_t time, int addr, int data ) | ||
132 | { | ||
133 | //assert( (unsigned) addr < reg_count ); | ||
134 | assert( ( addr >= 0x9800 && addr <= 0x988F ) || ( addr >= 0xB800 && addr <= 0xB8AF ) ); | ||
135 | run_until( this, time ); | ||
136 | |||
137 | addr -= 0x9800; | ||
138 | if ( ( unsigned ) addr < 0x90 ) | ||
139 | { | ||
140 | if ( ( unsigned ) addr < 0x60 ) | ||
141 | this->regs [addr] = data; | ||
142 | else if ( ( unsigned ) addr < 0x80 ) | ||
143 | { | ||
144 | this->regs [addr] = this->regs[addr + 0x20] = data; | ||
145 | } | ||
146 | else if ( ( unsigned ) addr < 0x90 ) | ||
147 | { | ||
148 | this->regs [addr + 0x20] = data; | ||
149 | } | ||
150 | } | ||
151 | else | ||
152 | { | ||
153 | addr -= 0xB800 - 0x9800; | ||
154 | if ( ( unsigned ) addr < 0xB0 ) | ||
155 | this->regs [addr] = data; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | void Scc_end_frame( struct Scc_Apu* this, blip_time_t end_time ) | ||
160 | { | ||
161 | if ( end_time > this->last_time ) | ||
162 | run_until( this, end_time ); | ||
163 | |||
164 | this->last_time -= end_time; | ||
165 | assert( this->last_time >= 0 ); | ||
166 | } | ||