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