Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libgambatte/src/sound.cpp
2 views
1
/***************************************************************************
2
* Copyright (C) 2007 by Sindre AamÄs *
3
* [email protected] *
4
* *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License version 2 as *
7
* published by the Free Software Foundation. *
8
* *
9
* This program is distributed in the hope that it will be useful, *
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12
* GNU General Public License version 2 for more details. *
13
* *
14
* You should have received a copy of the GNU General Public License *
15
* version 2 along with this program; if not, write to the *
16
* Free Software Foundation, Inc., *
17
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18
***************************************************************************/
19
#include "sound.h"
20
#include "savestate.h"
21
#include <cstring>
22
#include <algorithm>
23
24
/*
25
Frame Sequencer
26
27
Step Length Ctr Vol Env Sweep
28
- - - - - - - - - - - - - - - - - - - -
29
0 Clock - Clock
30
S 1 - Clock -
31
2 Clock - -
32
3 - - -
33
4 Clock - Clock
34
5 - - -
35
6 Clock - -
36
7 - - -
37
- - - - - - - - - - - - - - - - - - - -
38
Rate 256 Hz 64 Hz 128 Hz
39
40
S) start step on sound power on.
41
*/
42
43
namespace gambatte {
44
45
PSG::PSG()
46
: buffer(0),
47
lastUpdate(0),
48
soVol(0),
49
rsum(0x8000), // initialize to 0x8000 to prevent borrows from high word, xor away later
50
bufferPos(0),
51
enabled(false)
52
{
53
}
54
55
void PSG::init(const bool cgb) {
56
ch1.init(cgb);
57
ch2.init(cgb);
58
ch3.init(cgb);
59
ch4.init(cgb);
60
}
61
62
void PSG::reset() {
63
ch1.reset();
64
ch2.reset();
65
ch3.reset();
66
ch4.reset();
67
}
68
69
void PSG::setStatePtrs(SaveState &state) {
70
ch3.setStatePtrs(state);
71
}
72
73
void PSG::loadState(const SaveState &state) {
74
ch1.loadState(state);
75
ch2.loadState(state);
76
ch3.loadState(state);
77
ch4.loadState(state);
78
79
lastUpdate = state.cpu.cycleCounter;
80
set_so_volume(state.mem.ioamhram.get()[0x124]);
81
map_so(state.mem.ioamhram.get()[0x125]);
82
enabled = state.mem.ioamhram.get()[0x126] >> 7 & 1;
83
}
84
85
void PSG::accumulate_channels(const unsigned long cycles) {
86
uint_least32_t *const buf = buffer + bufferPos;
87
88
std::memset(buf, 0, cycles * sizeof(uint_least32_t));
89
ch1.update(buf, soVol, cycles);
90
ch2.update(buf, soVol, cycles);
91
ch3.update(buf, soVol, cycles);
92
ch4.update(buf, soVol, cycles);
93
}
94
95
void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doubleSpeed) {
96
const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
97
lastUpdate += cycles << (1 + doubleSpeed);
98
99
if (cycles)
100
accumulate_channels(cycles);
101
102
bufferPos += cycles;
103
}
104
105
void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, const unsigned doubleSpeed) {
106
generate_samples(oldCc, doubleSpeed);
107
lastUpdate = newCc - (oldCc - lastUpdate);
108
}
109
110
unsigned PSG::fillBuffer() {
111
uint_least32_t sum = rsum;
112
uint_least32_t *b = buffer;
113
unsigned n = bufferPos;
114
115
if (unsigned n2 = n >> 3) {
116
n -= n2 << 3;
117
118
do {
119
sum += b[0];
120
b[0] = sum ^ 0x8000;
121
sum += b[1];
122
b[1] = sum ^ 0x8000;
123
sum += b[2];
124
b[2] = sum ^ 0x8000;
125
sum += b[3];
126
b[3] = sum ^ 0x8000;
127
sum += b[4];
128
b[4] = sum ^ 0x8000;
129
sum += b[5];
130
b[5] = sum ^ 0x8000;
131
sum += b[6];
132
b[6] = sum ^ 0x8000;
133
sum += b[7];
134
b[7] = sum ^ 0x8000;
135
136
b += 8;
137
} while (--n2);
138
}
139
140
while (n--) {
141
sum += *b;
142
*b++ = sum ^ 0x8000; // xor away the initial rsum value of 0x8000 (which prevents borrows from the high word) from the low word
143
}
144
145
rsum = sum;
146
147
return bufferPos;
148
}
149
150
#ifdef WORDS_BIGENDIAN
151
static const unsigned long so1Mul = 0x00000001;
152
static const unsigned long so2Mul = 0x00010000;
153
#else
154
static const unsigned long so1Mul = 0x00010000;
155
static const unsigned long so2Mul = 0x00000001;
156
#endif
157
158
void PSG::set_so_volume(const unsigned nr50) {
159
soVol = (((nr50 & 0x7) + 1) * so1Mul + ((nr50 >> 4 & 0x7) + 1) * so2Mul) * 64;
160
}
161
162
void PSG::map_so(const unsigned nr51) {
163
const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
164
165
ch1.setSo((tmp & 0x00010001) * 0xFFFF);
166
ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF);
167
ch3.setSo((tmp >> 2 & 0x00010001) * 0xFFFF);
168
ch4.setSo((tmp >> 3 & 0x00010001) * 0xFFFF);
169
}
170
171
unsigned PSG::getStatus() const {
172
return ch1.isActive() | ch2.isActive() << 1 | ch3.isActive() << 2 | ch4.isActive() << 3;
173
}
174
175
// the buffer and position are not saved, as they're set and flushed on each runfor() call
176
SYNCFUNC(PSG)
177
{
178
SSS(ch1);
179
SSS(ch2);
180
SSS(ch3);
181
SSS(ch4);
182
NSS(lastUpdate);
183
NSS(soVol);
184
NSS(rsum);
185
NSS(enabled);
186
}
187
188
}
189
190