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