1
2// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/
3
4#include "Nonlinear_Buffer.h"
5
6#include "Nes_Apu.h"
7
8/* Library Copyright (C) 2003-2005 Shay Green. This library is free software;
9you can redistribute it and/or modify it under the terms of the GNU Lesser
10General Public License as published by the Free Software Foundation; either
11version 2.1 of the License, or (at your option) any later version. This
12module is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15details. You should have received a copy of the GNU Lesser General Public
16License along with this library; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18
19#include BLARGG_SOURCE_BEGIN
20
21// Nonlinear_Buffer
22
23Nonlinear_Buffer::Nonlinear_Buffer() :
24 Multi_Buffer( 1 )
25{
26}
27
28Nonlinear_Buffer::~Nonlinear_Buffer()
29{
30}
31
32void Nonlinear_Buffer::enable_nonlinearity( Nes_Apu& apu, bool b )
33{
34 if ( b )
35 clear();
36 nonlinearizer.enable( apu, b );
37 for ( int i = 0; i < apu.osc_count; i++ )
38 apu.osc_output( i, (i >= 2 ? &tnd : &buf) );
39}
40
41blargg_err_t Nonlinear_Buffer::sample_rate( long rate, int msec )
42{
43 BLARGG_RETURN_ERR( buf.sample_rate( rate, msec ) );
44 BLARGG_RETURN_ERR( tnd.sample_rate( rate, msec ) );
45 return Multi_Buffer::sample_rate( buf.sample_rate(), buf.length() );
46}
47
48void Nonlinear_Buffer::clock_rate( long rate )
49{
50 buf.clock_rate( rate );
51 tnd.clock_rate( rate );
52}
53
54void Nonlinear_Buffer::bass_freq( int freq )
55{
56 buf.bass_freq( freq );
57 tnd.bass_freq( freq );
58}
59
60void Nonlinear_Buffer::clear()
61{
62 nonlinearizer.clear();
63 buf.clear();
64 tnd.clear();
65}
66
67Nonlinear_Buffer::channel_t Nonlinear_Buffer::channel( int i )
68{
69 channel_t c;
70 c.center = &buf;
71 if ( 2 <= i && i <= 4 )
72 c.center = &tnd; // only use for triangle, noise, and dmc
73 c.left = c.center;
74 c.right = c.center;
75 return c;
76}
77
78void Nonlinear_Buffer::end_frame( blip_time_t length, bool )
79{
80 buf.end_frame( length );
81 tnd.end_frame( length );
82}
83
84long Nonlinear_Buffer::samples_avail() const
85{
86 return buf.samples_avail();
87}
88
89#include BLARGG_ENABLE_OPTIMIZER
90
91long Nonlinear_Buffer::read_samples( blip_sample_t* out, long count )
92{
93 count = nonlinearizer.make_nonlinear( tnd, count );
94 if ( count )
95 {
96 Blip_Reader lin;
97 Blip_Reader nonlin;
98
99 int lin_bass = lin.begin( buf );
100 int nonlin_bass = nonlin.begin( tnd );
101
102 for ( int n = count; n--; )
103 {
104 int s = lin.read() + nonlin.read();
105 lin.next( lin_bass );
106 nonlin.next( nonlin_bass );
107 *out++ = s;
108
109 if ( (BOOST::int16_t) s != s )
110 out [-1] = 0x7FFF - (s >> 24);
111 }
112
113 lin.end( buf );
114 nonlin.end( tnd );
115
116 buf.remove_samples( count );
117 tnd.remove_samples( count );
118 }
119
120 return count;
121}
122
123// Nes_Nonlinearizer
124
125Nes_Nonlinearizer::Nes_Nonlinearizer()
126{
127 nonlinear = false;
128
129 double gain = 0x7fff * 1.3;
130 // don't use entire range, so any overflow will stay within table
131 int const range = half * 0.75; // to do: must match that in Nes_Apu.cpp
132 for ( int i = 0; i < half * 2; i++ )
133 {
134 int out = i << shift;
135 if ( i > half )
136 {
137 int j = i - half;
138 if ( j >= range )
139 j = range - 1;
140 double n = 202.0 / (range - 1) * j;
141 double d = 163.67 / (24329.0 / n + 100);
142 out = int (d * gain) + 0x8000;
143 assert( out < 0x10000 );
144 }
145 table [i] = out;
146 }
147 clear();
148}
149
150void Nes_Nonlinearizer::enable( Nes_Apu& apu, bool b )
151{
152 nonlinear = b;
153 if ( b )
154 apu.enable_nonlinear( 1.0 );
155 else
156 apu.volume( 1.0 );
157}
158
159long Nes_Nonlinearizer::make_nonlinear( Blip_Buffer& buf, long count )
160{
161 long avail = buf.samples_avail();
162 if ( count > avail )
163 count = avail;
164
165 if ( count && nonlinear )
166 {
167 const int zero_offset = 0x7f7f; // to do: use private constant from Blip_Buffer.h
168
169 #define ENTRY( s ) (table [((s) >> shift) & entry_mask])
170
171 BOOST::uint16_t* p = buf.buffer_;
172 unsigned prev = ENTRY( accum );
173 long accum = this->accum;
174
175 for ( unsigned n = count; n--; )
176 {
177 accum += (long) *p - zero_offset;
178 check( (accum >> shift) < half * 2 );
179 unsigned entry = ENTRY( accum );
180 *p++ = entry - prev + zero_offset;
181 prev = entry;
182 }
183
184 this->accum = accum;
185 }
186
187 return count;
188}
189
190