1 
2 // Nes_Emu 0.7.0. http://www.slack.net/~ant/libs/
3 
4 #include "Nes_Buffer.h"
5 
6 #include "Nes_Apu.h"
7 
8 /* Library Copyright (C) 2003-2006 Shay Green. This library is free software;
9 you can redistribute it and/or modify it under the terms of the GNU Lesser
10 General Public License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version. This
12 module is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
15 details. You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18 
19 #include "blargg_source.h"
20 
21 #ifdef BLARGG_ENABLE_OPTIMIZER
22 	#include BLARGG_ENABLE_OPTIMIZER
23 #endif
24 
25 // Nes_Buffer
26 
Nes_Buffer()27 Nes_Buffer::Nes_Buffer() : Multi_Buffer( 1 ) { }
28 
~Nes_Buffer()29 Nes_Buffer::~Nes_Buffer() { }
30 
set_apu(Nes_Buffer * buf,Nes_Apu * apu)31 Multi_Buffer* set_apu( Nes_Buffer* buf, Nes_Apu* apu )
32 {
33 	buf->set_apu( apu );
34 	return buf;
35 }
36 
enable_nonlinearity(bool b)37 void Nes_Buffer::enable_nonlinearity( bool b )
38 {
39 	if ( b )
40 		clear();
41 
42 	Nes_Apu* apu = nonlin.enable( b, &tnd );
43 	apu->osc_output( 0, &buf );
44 	apu->osc_output( 1, &buf );
45 }
46 
set_sample_rate(long rate,int msec)47 const char * Nes_Buffer::set_sample_rate( long rate, int msec )
48 {
49 	enable_nonlinearity( nonlin.enabled ); // reapply
50 	RETURN_ERR( buf.set_sample_rate( rate, msec ) );
51 	RETURN_ERR( tnd.set_sample_rate( rate, msec ) );
52 	return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
53 }
54 
clock_rate(long rate)55 void Nes_Buffer::clock_rate( long rate )
56 {
57 	buf.clock_rate( rate );
58 	tnd.clock_rate( rate );
59 }
60 
bass_freq(int freq)61 void Nes_Buffer::bass_freq( int freq )
62 {
63 	buf.bass_freq( freq );
64 	tnd.bass_freq( freq );
65 }
66 
clear()67 void Nes_Buffer::clear()
68 {
69 	nonlin.clear();
70 	buf.clear();
71 	tnd.clear();
72 }
73 
channel(int i)74 Nes_Buffer::channel_t Nes_Buffer::channel( int i )
75 {
76 	channel_t c;
77 	c.center = &buf;
78 	if ( 2 <= i && i <= 4 )
79 		c.center = &tnd; // only use for triangle, noise, and dmc
80 	c.left   = c.center;
81 	c.right  = c.center;
82 	return c;
83 }
84 
end_frame(blip_time_t length,bool)85 void Nes_Buffer::end_frame( blip_time_t length, bool )
86 {
87 	buf.end_frame( length );
88 	tnd.end_frame( length );
89 }
90 
samples_avail() const91 long Nes_Buffer::samples_avail() const
92 {
93 	return buf.samples_avail();
94 }
95 
read_samples(blip_sample_t * out,long count)96 long Nes_Buffer::read_samples( blip_sample_t* out, long count )
97 {
98 	count = nonlin.make_nonlinear( tnd, count );
99 	if ( count )
100 	{
101 		Blip_Reader lin;
102 		Blip_Reader nonlin;
103 
104 		int lin_bass = lin.begin( buf );
105 		int nonlin_bass = nonlin.begin( tnd );
106 
107 		if (out != NULL)
108 		{
109 			for ( int n = count; n--; )
110 			{
111 				int s = lin.read() + nonlin.read();
112 				lin.next( lin_bass );
113 				nonlin.next( nonlin_bass );
114 				*out++ = s;
115 
116 				if ( (int16_t) s != s )
117 					out [-1] = 0x7FFF - (s >> 24);
118 			}
119 		}
120 		else
121 		{
122 			//only run accumulators, do not output audio
123 			for (int n = count; n--; )
124 			{
125 				lin.next(lin_bass);
126 				nonlin.next(nonlin_bass);
127 			}
128 		}
129 
130 		lin.end( buf );
131 		nonlin.end( tnd );
132 
133 		buf.remove_samples( count );
134 		tnd.remove_samples( count );
135 	}
136 
137 	return count;
138 }
139 
SaveAudioBufferState()140 void Nes_Buffer::SaveAudioBufferState()
141 {
142 	SaveAudioBufferStatePrivate();
143 	nonlin.SaveAudioBufferState();
144 	buf.SaveAudioBufferState();
145 	tnd.SaveAudioBufferState();
146 }
147 
RestoreAudioBufferState()148 void Nes_Buffer::RestoreAudioBufferState()
149 {
150 	RestoreAudioBufferStatePrivate();
151 	nonlin.RestoreAudioBufferState();
152 	buf.RestoreAudioBufferState();
153 	tnd.RestoreAudioBufferState();
154 }
155 
156 // Nes_Nonlinearizer
157 
Nes_Nonlinearizer()158 Nes_Nonlinearizer::Nes_Nonlinearizer()
159 {
160 	apu = NULL;
161 	enabled = true;
162 
163 	float const gain = 0x7fff * 1.3f;
164 	// don't use entire range, so any overflow will stay within table
165 	int const range = (int) (table_size * Nes_Apu::nonlinear_tnd_gain());
166 	for ( int i = 0; i < table_size; i++ )
167 	{
168 		int const offset = table_size - range;
169 		int j = i - offset;
170 		float n = 202.0f / (range - 1) * j;
171 		float d = 0;
172 		// Prevent division by zero
173 		if ( n )
174 			d = gain * 163.67f / (24329.0f / n + 100.0f);
175 		int out = (int) d;
176 		table [j & (table_size - 1)] = out;
177 	}
178 	extra_accum = 0;
179 	extra_prev = 0;
180 }
181 
enable(bool b,Blip_Buffer * buf)182 Nes_Apu* Nes_Nonlinearizer::enable( bool b, Blip_Buffer* buf )
183 {
184 	apu->osc_output( 2, buf );
185 	apu->osc_output( 3, buf );
186 	apu->osc_output( 4, buf );
187 	enabled = b;
188 	if ( b )
189 		apu->enable_nonlinear( 1.0 );
190 	else
191 		apu->volume( 1.0 );
192 	return apu;
193 }
194 
195 #define ENTRY( s ) table [(s) >> (blip_sample_bits - table_bits - 1) & (table_size - 1)]
196 
make_nonlinear(Blip_Buffer & buf,long count)197 long Nes_Nonlinearizer::make_nonlinear( Blip_Buffer& buf, long count )
198 {
199 	long avail = buf.samples_avail();
200 	if ( count > avail )
201 		count = avail;
202 	if ( count && enabled )
203 	{
204 
205 		Blip_Buffer::buf_t_* p = buf.buffer_;
206 		long accum = this->accum;
207 		long prev = this->prev;
208 		for ( unsigned n = count; n; --n )
209 		{
210 			long entry = ENTRY( accum );
211 			accum += *p;
212 			*p++ = (entry - prev) << (blip_sample_bits - 16);
213 			prev = entry;
214 		}
215 
216 		this->prev = prev;
217 		this->accum = accum;
218 	}
219 
220 	return count;
221 }
222 
clear()223 void Nes_Nonlinearizer::clear()
224 {
225 	accum = 0;
226 	prev = ENTRY( 86016000 ); // avoid thump due to APU's triangle dc bias
227 	// TODO: still results in slight clicks and thumps
228 }
229 
SaveAudioBufferState()230 void Nes_Nonlinearizer::SaveAudioBufferState()
231 {
232 	extra_accum = accum;
233 	extra_prev = prev;
234 }
235 
RestoreAudioBufferState()236 void Nes_Nonlinearizer::RestoreAudioBufferState()
237 {
238 	accum = extra_accum;
239 	prev = extra_prev;
240 }
241