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; |
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_BEGIN |
20 | |
21 | // Nonlinear_Buffer |
22 | |
23 | Nonlinear_Buffer::Nonlinear_Buffer() : |
24 | Multi_Buffer( 1 ) |
25 | { |
26 | } |
27 | |
28 | Nonlinear_Buffer::~Nonlinear_Buffer() |
29 | { |
30 | } |
31 | |
32 | void 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 | |
41 | blargg_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 | |
48 | void Nonlinear_Buffer::clock_rate( long rate ) |
49 | { |
50 | buf.clock_rate( rate ); |
51 | tnd.clock_rate( rate ); |
52 | } |
53 | |
54 | void Nonlinear_Buffer::bass_freq( int freq ) |
55 | { |
56 | buf.bass_freq( freq ); |
57 | tnd.bass_freq( freq ); |
58 | } |
59 | |
60 | void Nonlinear_Buffer::clear() |
61 | { |
62 | nonlinearizer.clear(); |
63 | buf.clear(); |
64 | tnd.clear(); |
65 | } |
66 | |
67 | Nonlinear_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 | |
78 | void Nonlinear_Buffer::end_frame( blip_time_t length, bool ) |
79 | { |
80 | buf.end_frame( length ); |
81 | tnd.end_frame( length ); |
82 | } |
83 | |
84 | long Nonlinear_Buffer::samples_avail() const |
85 | { |
86 | return buf.samples_avail(); |
87 | } |
88 | |
89 | #include BLARGG_ENABLE_OPTIMIZER |
90 | |
91 | long 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 | |
125 | Nes_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 | |
150 | void 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 | |
159 | long 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 | |