openMSX
YM2413Test.cc
Go to the documentation of this file.
1#include "YM2413Okazaki.hh"
2#include "YM2413Burczynski.hh"
3#include "WavWriter.hh"
4#include "WavData.hh"
5#include "Filename.hh"
6#include <cstdint>
7#include <vector>
8#include <string>
9#include <iostream>
10
11using namespace std;
12using namespace openmsx;
13
14
15// global vars
16string coreName;
17string testName;
18
19
20constexpr unsigned CHANNELS = 11;
21
22
24{
25 RegWrite(byte reg_, byte val_) : reg(reg_), val(val_) {}
26 byte reg;
27 byte val;
28};
29using RegWrites = vector<RegWrite>;
31{
32 vector<RegWrite> regWrites;
33 unsigned samples; // number of samples between this and next event
34};
35using Log = vector<LogEvent>;
36using Samples = vector<int>;
37
38
39static void error(const string& message)
40{
41 cout << message << '\n';
42}
43
44
45static void saveWav(const string& filename, const Samples& data)
46{
47 WavWriter writer(Filename(filename), 1, 16, 3579545 / 72);
48 writer.write16mono(&data[0], data.size());
49}
50
51static void loadWav(const string& filename, Samples& data)
52{
53 WavData wav(filename);
54 assert(wav.getFreq() == 3579545 / 72);
55
56 auto rawData = wav.getData();
57 data.assign(rawData, rawData + wav.getSize());
58}
59
60static void loadWav(Samples& data)
61{
62 string filename = coreName + '-' + testName + ".wav";
63 loadWav(filename, data);
64}
65
66static void createSilence(const Log& log, Samples& result)
67{
68 unsigned size = 0;
69 for (auto& l : log) {
70 size += l.samples;
71 }
72 result.resize(size);
73}
74
75
76static void test(YM2413Core& core, const Log& log,
77 const Samples* expectedSamples[CHANNELS])
78{
79 cout << " test " << testName << " ...\n";
80
81 Samples generatedSamples[CHANNELS];
82
83 for (auto& l : log) {
84 // write registers
85 for (auto& w : l.regWrites) {
86 core.writeReg(w.reg, w.val);
87 }
88
89 unsigned samples = l.samples;
90
91 // setup buffers
92 int* bufs[CHANNELS];
93 unsigned oldSize = generatedSamples[0].size();
94 for (unsigned i = 0; i < CHANNELS; ++i) {
95 generatedSamples[i].resize(oldSize + samples);
96 bufs[i] = &generatedSamples[i][oldSize];
97 }
98
99 // actually generate samples
100 core.generateChannels(bufs, samples);
101 }
102
103 // amplify generated data
104 // (makes comparison between different cores easier)
105 unsigned factor = core.getAmplificationFactor();
106 for (unsigned i = 0; i < CHANNELS; ++i) {
107 for (unsigned j = 0; j < generatedSamples[i].size(); ++j) {
108 int s = generatedSamples[i][j];
109 s *= factor;
110 assert(s == int16_t(s)); // shouldn't overflow 16-bit
111 generatedSamples[i][j] = s;
112 }
113 }
114
115 // verify generated samples
116 for (unsigned i = 0; i < CHANNELS; ++i) {
117 string msg = strCat("Error in channel ", i, ": ");
118 bool err = false;
119 if (generatedSamples[i].size() != expectedSamples[i]->size()) {
120 strAppend(msg, "wrong size, expected ", expectedSamples[i]->size(),
121 " but got ", generatedSamples[i].size());
122 err = true;
123 } else if (generatedSamples[i] != *expectedSamples[i]) {
124 strAppend(msg, "Wrong data");
125 err = true;
126 }
127 if (err) {
128 string filename = strCat(
129 "bad-", coreName, '-', testName,
130 "-ch", i, ".wav");
131 strAppend(msg, " writing data to ", filename);
132 error(msg);
133 saveWav(filename, generatedSamples[i]);
134 }
135 }
136}
137
138static void testSingleChannel(YM2413Core& core, const Log& log,
139 const Samples& channelData, unsigned channelNum)
140{
141 Samples silence;
142 createSilence(log, silence);
143
144 const Samples* samples[CHANNELS];
145 for (unsigned i = 0; i < CHANNELS; ++i) {
146 if (i == channelNum) {
147 samples[i] = &channelData;
148 } else {
149 samples[i] = &silence;
150 }
151 }
152
153 test(core, log, samples);
154}
155
156
157static void testSilence(YM2413Core& core)
158{
159 testName = "silence";
160 Log log;
161 {
162 LogEvent event;
163 // no register writes
164 event.samples = 1000;
165 log.push_back(event);
166 }
167 Samples silence;
168 createSilence(log, silence);
169
170 const Samples* samples[CHANNELS];
171 for (unsigned i = 0; i < CHANNELS; ++i) {
172 samples[i] = &silence;
173 }
174
175 test(core, log, samples);
176}
177
178static void testViolin(YM2413Core& core)
179{
180 testName = "violin";
181 Log log;
182 {
183 LogEvent event;
184 event.regWrites.emplace_back(0x30, 0x10); // instrument / volume
185 event.regWrites.emplace_back(0x10, 0xAD); // frequency
186 event.regWrites.emplace_back(0x20, 0x14); // key-on / frequency
187 event.samples = 11000;
188 log.push_back(event);
189 }
190 {
191 LogEvent event;
192 event.regWrites.emplace_back(0x20, 0x16); // change freq
193 event.samples = 11000;
194 log.push_back(event);
195 }
196 {
197 LogEvent event;
198 event.regWrites.emplace_back(0x20, 0x06); // key-off
199 event.samples = 11000;
200 log.push_back(event);
201 }
202 Samples gold;
203 loadWav(gold);
204
205 testSingleChannel(core, log, gold, 0);
206}
207
208template<typename CORE, typename FUNC> void testOnCore(FUNC f)
209{
210 CORE core;
211 f(core);
212}
213
214template<typename CORE> static void testAll(const string& coreName_)
215{
216 coreName = coreName_;
217 cout << "Testing YM2413 core " << coreName << '\n';
218 testOnCore<CORE>(testSilence);
219 testOnCore<CORE>(testViolin);
220 cout << '\n';
221}
222
223int main()
224{
225 testAll<YM2413Okazaki:: YM2413>("Okazaki");
226 testAll<YM2413Burczynski::YM2413>("Burczynski");
227 return 0;
228}
string coreName
Definition: YM2413Test.cc:16
vector< int > Samples
Definition: YM2413Test.cc:36
vector< RegWrite > RegWrites
Definition: YM2413Test.cc:29
void testOnCore(FUNC f)
Definition: YM2413Test.cc:208
vector< LogEvent > Log
Definition: YM2413Test.cc:35
int main()
Definition: YM2413Test.cc:223
constexpr unsigned CHANNELS
Definition: YM2413Test.cc:20
string testName
Definition: YM2413Test.cc:17
Base class for writing WAV files.
Definition: WavWriter.hh:16
Abstract interface for the YM2413 core.
Definition: YM2413Core.hh:27
virtual float getAmplificationFactor() const =0
Returns normalization factor.
virtual void generateChannels(float *bufs[11], unsigned num)=0
Generate the sound output.
constexpr double log(double x)
Definition: cstd.hh:207
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr const char *const filename
STL namespace.
size_t size(std::string_view utf8)
std::string strCat(Ts &&...ts)
Definition: strCat.hh:549
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:627
unsigned samples
Definition: YM2413Test.cc:33
vector< RegWrite > regWrites
Definition: YM2413Test.cc:32
byte val
Definition: YM2413Test.cc:27
byte reg
Definition: YM2413Test.cc:26
RegWrite(byte reg_, byte val_)
Definition: YM2413Test.cc:25