1 #include "Nes_Vrc7_Apu.h"
2
3 #include "ym2413.h"
4 #include <string.h>
5
6 #include "blargg_source.h"
7
8 int const period = 36; // NES CPU clocks per FM clock
9
Nes_Vrc7_Apu()10 Nes_Vrc7_Apu::Nes_Vrc7_Apu()
11 {
12 opll = 0;
13 }
14
init()15 blargg_err_t Nes_Vrc7_Apu::init()
16 {
17 CHECK_ALLOC( opll = ym2413_init( 3579545, 3579545 / 72, 1 ) );
18
19 set_output( 0 );
20 volume( 1.0 );
21 reset();
22 return 0;
23 }
24
~Nes_Vrc7_Apu()25 Nes_Vrc7_Apu::~Nes_Vrc7_Apu()
26 {
27 if ( opll )
28 ym2413_shutdown( opll );
29 }
30
set_output(Blip_Buffer * buf)31 void Nes_Vrc7_Apu::set_output( Blip_Buffer* buf )
32 {
33 for ( int i = 0; i < osc_count; ++i )
34 oscs [i].output = buf;
35 output_changed();
36 }
37
output_changed()38 void Nes_Vrc7_Apu::output_changed()
39 {
40 mono.output = oscs [0].output;
41 for ( int i = osc_count; --i; )
42 {
43 if ( mono.output != oscs [i].output )
44 {
45 mono.output = 0;
46 break;
47 }
48 }
49
50 if ( mono.output )
51 {
52 for ( int i = osc_count; --i; )
53 {
54 mono.last_amp += oscs [i].last_amp;
55 oscs [i].last_amp = 0;
56 }
57 }
58 }
59
reset()60 void Nes_Vrc7_Apu::reset()
61 {
62 addr = 0;
63 next_time = 0;
64 mono.last_amp = 0;
65
66 for ( int i = osc_count; --i >= 0; )
67 {
68 Vrc7_Osc& osc = oscs [i];
69 osc.last_amp = 0;
70 for ( int j = 0; j < 3; ++j )
71 osc.regs [j] = 0;
72 }
73
74 ym2413_reset_chip( opll );
75 }
76
write_reg(int data)77 void Nes_Vrc7_Apu::write_reg( int data )
78 {
79 addr = data;
80 }
81
write_data(blip_time_t time,int data)82 void Nes_Vrc7_Apu::write_data( blip_time_t time, int data )
83 {
84 int type = (addr >> 4) - 1;
85 int chan = addr & 15;
86 if ( (unsigned) type < 3 && chan < osc_count )
87 oscs [chan].regs [type] = data;
88
89 if ( time > next_time )
90 run_until( time );
91 ym2413_write( opll, 0, addr );
92 ym2413_write( opll, 1, data );
93 }
94
end_frame(blip_time_t time)95 void Nes_Vrc7_Apu::end_frame( blip_time_t time )
96 {
97 if ( time > next_time )
98 run_until( time );
99
100 next_time -= time;
101 assert( next_time >= 0 );
102
103 for ( int i = osc_count; --i >= 0; )
104 {
105 Blip_Buffer* output = oscs [i].output;
106 if ( output )
107 output->set_modified();
108 }
109 }
110
save_snapshot(vrc7_snapshot_t * out) const111 void Nes_Vrc7_Apu::save_snapshot( vrc7_snapshot_t* out ) const
112 {
113 out->latch = addr;
114 out->delay = next_time;
115 for ( int i = osc_count; --i >= 0; )
116 {
117 for ( int j = 0; j < 3; ++j )
118 out->regs [i] [j] = oscs [i].regs [j];
119 }
120 memcpy( out->inst, ym2413_get_inst0( opll ), 8 );
121 }
122
load_snapshot(vrc7_snapshot_t const & in)123 void Nes_Vrc7_Apu::load_snapshot( vrc7_snapshot_t const& in )
124 {
125 assert( offsetof (vrc7_snapshot_t,delay) == 28 - 1 );
126
127 reset();
128 next_time = in.delay;
129 write_reg( in.latch );
130 int i;
131 for ( i = 0; i < osc_count; ++i )
132 {
133 for ( int j = 0; j < 3; ++j )
134 oscs [i].regs [j] = in.regs [i] [j];
135 }
136
137 for ( i = 0; i < 8; ++i )
138 {
139 ym2413_write( opll, 0, i );
140 ym2413_write( opll, 1, in.inst [i] );
141 }
142
143 for ( i = 0; i < 3; ++i )
144 {
145 for ( int j = 0; j < 6; ++j )
146 {
147 ym2413_write( opll, 0, 0x10 + i * 0x10 + j );
148 ym2413_write( opll, 1, oscs [j].regs [i] );
149 }
150 }
151 }
152
run_until(blip_time_t end_time)153 void Nes_Vrc7_Apu::run_until( blip_time_t end_time )
154 {
155 require( end_time > next_time );
156
157 blip_time_t time = next_time;
158 void* opll = this->opll; // cache
159 Blip_Buffer* const mono_output = mono.output;
160 if ( mono_output )
161 {
162 // optimal case
163 do
164 {
165 ym2413_advance_lfo( opll );
166 int amp = 0;
167 for ( int i = 0; i < osc_count; i++ )
168 amp += ym2413_calcch( opll, i );
169 ym2413_advance( opll );
170 int delta = amp - mono.last_amp;
171 if ( delta )
172 {
173 mono.last_amp = amp;
174 synth.offset_inline( time, delta, mono_output );
175 }
176 time += period;
177 }
178 while ( time < end_time );
179 }
180 else
181 {
182 mono.last_amp = 0;
183 do
184 {
185 ym2413_advance_lfo( opll );
186 for ( int i = 0; i < osc_count; ++i )
187 {
188 Vrc7_Osc& osc = oscs [i];
189 if ( osc.output )
190 {
191 int amp = ym2413_calcch( opll, i );
192 int delta = amp - osc.last_amp;
193 if ( delta )
194 {
195 osc.last_amp = amp;
196 synth.offset( time, delta, osc.output );
197 }
198 }
199 }
200 ym2413_advance( opll );
201 time += period;
202 }
203 while ( time < end_time );
204 }
205 next_time = time;
206 }
207