summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/kss_scc_apu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/kss_scc_apu.c')
-rw-r--r--lib/rbcodec/codecs/libgme/kss_scc_apu.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/kss_scc_apu.c b/lib/rbcodec/codecs/libgme/kss_scc_apu.c
new file mode 100644
index 0000000000..1bec9b7f0e
--- /dev/null
+++ b/lib/rbcodec/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
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#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).
20extern int const inaudible_freq;
21
22int const wave_size = 0x20;
23
24static 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
31void Scc_volume( struct Scc_Apu* this, int v )
32{
33 Synth_volume( &this->synth, (v/2 - (v*7)/100) / scc_osc_count / scc_amp_range );
34}
35
36void 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
47void Scc_init( struct Scc_Apu* this )
48{
49 Synth_init( &this->synth);
50
51 set_output( this, NULL );
52 Scc_volume( this, (int)FP_ONE_VOLUME );
53 Scc_reset( this );
54}
55
56static 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
131void 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
159void 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}