Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/quicknes/nes_emu/Nes_Buffer.cpp
2 views
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
27
Nes_Buffer::Nes_Buffer() : Multi_Buffer( 1 ) { }
28
29
Nes_Buffer::~Nes_Buffer() { }
30
31
Multi_Buffer* set_apu( Nes_Buffer* buf, Nes_Apu* apu )
32
{
33
buf->set_apu( apu );
34
return buf;
35
}
36
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
47
blargg_err_t 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
55
void Nes_Buffer::clock_rate( long rate )
56
{
57
buf.clock_rate( rate );
58
tnd.clock_rate( rate );
59
}
60
61
void Nes_Buffer::bass_freq( int freq )
62
{
63
buf.bass_freq( freq );
64
tnd.bass_freq( freq );
65
}
66
67
void Nes_Buffer::clear()
68
{
69
nonlin.clear();
70
buf.clear();
71
tnd.clear();
72
}
73
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
85
void Nes_Buffer::end_frame( blip_time_t length, bool )
86
{
87
buf.end_frame( length );
88
tnd.end_frame( length );
89
}
90
91
long Nes_Buffer::samples_avail() const
92
{
93
return buf.samples_avail();
94
}
95
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
for ( int n = count; n--; )
108
{
109
int s = lin.read() + nonlin.read();
110
lin.next( lin_bass );
111
nonlin.next( nonlin_bass );
112
*out++ = s;
113
114
if ( (BOOST::int16_t) s != s )
115
out [-1] = 0x7FFF - (s >> 24);
116
}
117
118
lin.end( buf );
119
nonlin.end( tnd );
120
121
buf.remove_samples( count );
122
tnd.remove_samples( count );
123
}
124
125
return count;
126
}
127
128
// Nes_Nonlinearizer
129
130
Nes_Nonlinearizer::Nes_Nonlinearizer()
131
{
132
apu = NULL;
133
enabled = true;
134
135
float const gain = 0x7fff * 1.3f;
136
// don't use entire range, so any overflow will stay within table
137
int const range = (int) (table_size * Nes_Apu::nonlinear_tnd_gain());
138
for ( int i = 0; i < table_size; i++ )
139
{
140
int const offset = table_size - range;
141
int j = i - offset;
142
float n = 202.0f / (range - 1) * j;
143
float d = gain * 163.67f / (24329.0f / n + 100.0f);
144
int out = (int) d;
145
//out = j << (15 - table_bits); // make table linear for testing
146
assert( out < 0x8000 );
147
table [j & (table_size - 1)] = out;
148
}
149
}
150
151
Nes_Apu* Nes_Nonlinearizer::enable( bool b, Blip_Buffer* buf )
152
{
153
require( apu );
154
apu->osc_output( 2, buf );
155
apu->osc_output( 3, buf );
156
apu->osc_output( 4, buf );
157
enabled = b;
158
if ( b )
159
apu->enable_nonlinear( 1.0 );
160
else
161
apu->volume( 1.0 );
162
return apu;
163
}
164
165
#define ENTRY( s ) table [(s) >> (blip_sample_bits - table_bits - 1) & (table_size - 1)]
166
167
long Nes_Nonlinearizer::make_nonlinear( Blip_Buffer& buf, long count )
168
{
169
require( apu );
170
long avail = buf.samples_avail();
171
if ( count > avail )
172
count = avail;
173
if ( count && enabled )
174
{
175
176
Blip_Buffer::buf_t_* p = buf.buffer_;
177
long accum = this->accum;
178
long prev = this->prev;
179
for ( unsigned n = count; n; --n )
180
{
181
long entry = ENTRY( accum );
182
check( (entry >= 0) == (accum >= 0) );
183
accum += *p;
184
*p++ = (entry - prev) << (blip_sample_bits - 16);
185
prev = entry;
186
}
187
188
this->prev = prev;
189
this->accum = accum;
190
}
191
192
return count;
193
}
194
195
void Nes_Nonlinearizer::clear()
196
{
197
accum = 0;
198
prev = ENTRY( 86016000 ); // avoid thump due to APU's triangle dc bias
199
// TODO: still results in slight clicks and thumps
200
}
201
202
203