-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapu.cpp
241 lines (192 loc) · 6.67 KB
/
apu.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#include <stdexcept>
#include <string>
#include "apu.hpp"
void checkPaError(PaError err) {
if (err != paNoError) {
fprintf(stderr, "PortAudio error: %s\n", Pa_GetErrorText(err));
exit(1);
}
}
static int apuCallback (const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData ) {
APU *apu = (APU *) userData;
float *out = (float *) outputBuffer;
(void) inputBuffer; /* Prevent unused variable warning. */
for(int i = 0; i < framesPerBuffer; i++) {
*out++ = apu->lastSample;
*out++ = apu->lastSample;
apu->tick();
}
return 0;
}
APU::APU(float sampleRate)
: time(0), sampleRate(sampleRate), timeStep(1.0/sampleRate),
frameCounterMode(0),
pulses(std::vector<PulseWave>(2, PulseWave(sampleRate))),
triangle(TriangleWave(sampleRate))
{
}
APU::~APU(void) {
PaError err = Pa_StopStream(stream);
checkPaError(err);
err = Pa_Terminate();
checkPaError(err);
}
void APU::apuInit(void) {
PaError err = Pa_Initialize();
checkPaError(err);
err = Pa_OpenDefaultStream(
&stream,
0, /* no input channels */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
(int) sampleRate, /* sample rate */
256, /* frames per buffer */
apuCallback, /* callback */
this); /* pointer passed to callback */
checkPaError(err);
err = Pa_StartStream(stream);
checkPaError(err);
}
// Computes one sample. Returns the sample, and also stores it in the
// lastSample member. Automatically advances the stored time. Mixes
// sources using a linear approximation of the NES's mixer. Returns a
// value between 0.0 and 1.0, so that zero outputs match (and there's
// no popping sound on startup and shutdown). Does not currently
// simulate the high-pass and low-pass filters that the NES appliles
// after DAC conversion.
float APU::tick(void) {
float out = 0.0;
// Using linear approximation for mixing: see
// http://wiki.nesdev.com/w/index.php/APU_Mixer
for (int pulse_i = 0; pulse_i < N_PULSE_WAVES; pulse_i++) {
out += pulses[pulse_i].tick() * PULSE_MIX_COEFFICIENT;
}
out += triangle.tick() * TRIANGLE_MIX_COEFFICIENT;
//out = (out * 2.0) - 1.0;
lastSample = out;
time += timeStep;
return out;
}
void APU::updateFrameCounter(bool mode) {
frameCounterMode = mode;
for (int i = 0; i < N_PULSE_WAVES; i++) {
pulses.at(i).updateFrameCounter(mode);
}
triangle.updateFrameCounter(mode);
if (mode) {
// Updating the frame counter with the mode bit set will
// immediately generate half-frame and quarter-frame clocks.
frameCounterHalfFrame();
}
}
void APU::frameCounterQuarterFrame() {
for (int i = 0; i < N_PULSE_WAVES; i++) {
pulses.at(i).frameCounterQuarterFrame();
}
triangle.frameCounterQuarterFrame();
}
void APU::frameCounterHalfFrame() {
for (int i = 0; i < N_PULSE_WAVES; i++) {
pulses.at(i).frameCounterHalfFrame();
}
triangle.frameCounterHalfFrame();
}
void APU::resetPulse(unsigned int pulse_n) {
pulses.at(pulse_n).reset();
}
void APU::setPulseDivider(unsigned int pulse_n, unsigned int divider) {
pulses.at(pulse_n).setDivider(divider);
}
void APU::setPulseEnabled(unsigned int pulse_n, bool enabled) {
pulses.at(pulse_n).setEnabled(enabled);
}
void APU::setPulseDuty(unsigned int pulse_n, float duty) {
pulses.at(pulse_n).setDuty(duty);
}
void APU::setPulseLengthCounterHalt(unsigned int pulse_n, bool h) {
pulses.at(pulse_n).setLengthCounterHalt(h);
}
void APU::setPulseLengthCounter(unsigned int pulse_n, unsigned int c) {
pulses.at(pulse_n).setLengthCounter(c);
}
void APU::updatePulseSweep(unsigned int pulse_n,
bool enabled, unsigned int divider,
unsigned int shift, bool negate) {
pulses.at(pulse_n).updateSweep(enabled, divider, shift, negate);
}
void APU::updatePulseEnvelope(unsigned int pulse_n,
bool loop, bool constant,
unsigned char timerReload) {
pulses.at(pulse_n).updateEnvelope(loop, constant, timerReload);
}
// ctypes interface
extern "C" {
APU *ex_initAPU(void) {
APU *out = new APU(SAMPLE_RATE);
out->apuInit();
return out;
}
void ex_updateFrameCounter(APU *apu, unsigned char mode) {
apu->updateFrameCounter((bool) mode);
}
void ex_frameCounterQuarterFrame(APU *apu) {
apu->frameCounterQuarterFrame();
}
void ex_frameCounterHalfFrame(APU *apu) {
apu->frameCounterHalfFrame();
}
// pulse wave interface
void ex_resetPulse(APU *apu, unsigned int pulse_n) {
apu->resetPulse(pulse_n);
}
void ex_setPulseDivider(APU *apu, unsigned int pulse_n, unsigned int divider) {
apu->setPulseDivider(pulse_n, divider);
}
void ex_setPulseEnabled(APU *apu, unsigned int pulse_n, unsigned char enabled) {
apu->setPulseEnabled(pulse_n, enabled);
}
void ex_setPulseDuty(APU *apu, unsigned int pulse_n, float duty) {
apu->setPulseDuty(pulse_n, duty);
}
void ex_setPulseLengthCounterHalt(APU *apu, unsigned int pulse_n, unsigned char halt) {
apu->setPulseLengthCounterHalt(pulse_n, (bool) halt);
}
void ex_setPulseLengthCounter(APU *apu, unsigned int pulse_n, unsigned int c) {
apu->setPulseLengthCounter(pulse_n, c);
}
void ex_updatePulseSweep(APU *apu, unsigned int pulse_n,
unsigned char enabled, unsigned int divider,
unsigned int shift, unsigned char negate) {
apu->updatePulseSweep(pulse_n,
(bool) enabled, divider,
shift, (bool) negate);
}
void ex_updatePulseEnvelope(APU *apu, unsigned int pulse_n,
unsigned char loop, unsigned char constant,
unsigned char timerReload) {
apu->updatePulseEnvelope(pulse_n, (bool) loop, (bool) constant, timerReload);
}
// triangle wave interface
void ex_setTriangleEnabled(APU *apu, unsigned char enabled) {
apu->triangle.setEnabled((bool) enabled);
}
void ex_setTriangleDivider(APU *apu, unsigned int divider) {
apu->triangle.setDivider(divider);
}
void ex_setTriangleLinearCounterInit(APU *apu, unsigned int c) {
apu->triangle.setLinearCounterInit(c);
}
void ex_setTriangleTimerHalts(APU *apu, unsigned char halt) {
apu->triangle.setTimerHalts((bool) halt);
}
void ex_setTriangleLengthCounter(APU *apu, unsigned int c) {
apu->triangle.setLengthCounter(c);
}
void ex_triangleLinearCounterReload(APU *apu) {
apu->triangle.linearCounterReload();
}
}