OXYGENHUB
◆ main openmsx / src / sound / YM2413Core.hh
6.4 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef YM2413CORE_HH
2
#define YM2413CORE_HH
3
4
#include <cstdint>
5
#include <span>
6
7
namespace openmsx {
8
9
/** Abstract interface for the YM2413 core.
10
 *
11
 * We currently have two concrete implementations:
12
 *  - YM2413Okazaki
13
 *  - YM2413Burczynski
14
 * The long term goal is to study the difference between these two
15
 * implementations and merge the best parts of either core into a single core.
16
 *
17
 * This interface separates the actual YM2413 emulation from the rest of the
18
 * (sound) emulation details. This allows to more easily share this
19
 * implementation between different emulators. It also allows to more easily
20
 * test the YM2413 emulation in isolation.
21
 *
22
 * There are two main functions in this interface: write to registers and get
23
 * output samples. All timing information is implicit in the order of the calls
24
 * to these functions (e.g. write some register, generate 10 output samples,
25
 * write another register, ...).
26
 */
27
class YM2413Core
28
{
29
public:
30
	/** Input clock frequency.
31
	 * An output sample is generated every 72 cycles. So the output sample
32
	 * frequency is effectively 49716Hz. If you need the output at e.g.
33
	 * 44100Hz you need to resample the data. Generating the output at the
34
	 * native YM2413 frequency allows for quite some simplifications in the
35
	 * implementation of the cores. It also generally results in better
36
	 * sound quality. At least if the resampler step itself doesn't degrade
37
	 * the quality again (Certainly don't simply skip some of the samples
38
	 * to get to 44kHz, at the very least do linear interpolation. But
39
	 * preferably use a better resample algorithm).
40
	 */
41
	static constexpr int CLOCK_FREQ = 3579545;
42
43
	virtual ~YM2413Core() = default;
44
45
	/** Reset this YM2413 core.
46
	 */
47
	virtual void reset() = 0;
48
49
	/** Write to the YM2413 register/data port.
50
	 * Writing to a register is a 2-step process: First write the register
51
	 * number to the register port (0), then write the value for that
52
	 * register to the data port (1). There are various timing constraints
53
	 * that must be taken into account (not described here).
54
	 *
55
	 * We emulate the YM2413 as-if it generates a stream at 49.7kHz and at
56
	 * each step it produces one sample for each of the 9+5 sound channels.
57
	 * Though in reality the YM2413 spreads out the generation of the
58
	 * channels over time. So it's like a stream at 18 x 49.7kHz where the
59
	 * 9+5 channels have a fixed position in the 18 possible slots (so not
60
	 * all 18 slots produce a (non-zero) output).
61
	 *
62
	 * In other words: we emulate the YM2413 18 steps at a time. Though for
63
	 * accurate emulation we still need to take the exact timing of
64
	 * register writes into account. That's what the 'offset' parameter is
65
	 * for. It must have a value [0..17] and indicates the sub-sample
66
	 * timing of the port-write.
67
	 */
68
	virtual void writePort(bool port, uint8_t value, int offset) = 0;
69
70
	/** Write to a YM2413 register (for debug).
71
	 * Because of the timing constraint on writing registers via
72
	 * writePort(), writing registers via that way is not very suited
73
	 * for use in a debugger. Use this method as an alternative.
74
	 */
75
	virtual void pokeReg(uint8_t reg, uint8_t value) = 0;
76
77
	/** Read from a YM2413 register (for debug).
78
	 * Note that the real YM2413 chip doesn't allow to read the registers.
79
	 * This returns the last written value or the default value if this
80
	 * register hasn't been written to since the last reset() call.
81
	 * Reading registers has no influence on the generated sound.
82
	 */
83
	[[nodiscard]] virtual std::span<const uint8_t, 64> peekRegs() const = 0;
84
85
	/** Generate the sound output.
86
	 * @param bufs Pointers to output buffers.
87
	 * @param num The number of required output samples.
88
	 *
89
	 * The requested number of samples must be strictly bigger than zero.
90
	 *
91
	 * The output of the different channels is put in separate output
92
	 * buffers. This makes it possible to e.g. record individual channels
93
	 * or to pan, mute or adjust volume per channel. The YM2413 can operate
94
	 * in two modes: 9 channels or 6 channels + 5 drum channels. The latter
95
	 * mode requires a total of 11 output buffers. In the first mode, the
96
	 * last two output buffers are filled with silence (this is very
97
	 * efficient, see below). Each output buffer should be big enough to
98
	 * hold at least 'num' number of ints.
99
	 *
100
	 * The output is not simply stored in the buffer, but added to the
101
	 * existing data in the buffer. So you'll have to zero the content
102
	 * of the buffer before passing it to this function. OTOH if you want
103
	 * to combine (some of) the channels, you can pass the same buffer for
104
	 * all those channels. This approach may have a little overhead when
105
	 * you're interested in all channels, but it is very efficient if
106
	 * you're only interested in the combined channels (the most common
107
	 * case). You can even directly combine this output with other chips
108
	 * with the same native frequency (like Y8950 or YMF262).
109
	 *
110
	 * When the core detects that some channel is silent, it will assign
111
	 * nullptr to the buffer pointer (so the content of the buffer is left
112
	 * unchanged, but the pointer to that buffer is set to zero). When all
113
	 * the channels you're interested in are silent you can even skip all
114
	 * subsequent audio processing for this channel (e.g. skip resampling to
115
	 * 44kHz). It is very common that some or even all of the channels are
116
	 * silent, so it's definitely worth it to implement this optimization.
117
	 * Also the cores internally try to detect silent channels very early,
118
	 * so an idle YM2413 core generally requires very little emulation
119
	 * time.
120
	 */
121
	virtual void generateChannels(std::span<float*, 9 + 5> bufs, unsigned num) = 0;
122
123
	/** Returns normalization factor.
124
	 * The output of the generateChannels() method should still be
125
	 * amplified (=multiplied) with this factor to get a consistent volume
126
	 * level across the different implementations of the YM2413 core. This
127
	 * allows to internally calculate with native volume levels, and
128
	 * possibly results in slightly simpler and/or faster code. It's very
129
	 * likely that subsequent audio processing steps (like resampler,
130
	 * filters or volume adjustments) must anyway still multiply the output
131
	 * sample values, so this factor can be folded-in for free.
132
	 */
133
	[[nodiscard]] virtual float getAmplificationFactor() const = 0;
134
135
	/** Sets real-time speed factor (aka the openMSX 'speed' setting).
136
	 * The default implementation does nothing. But e.g. the NukeYKT core
137
	 * needs this.
138
	 */
139
	virtual void setSpeed(double /*speed*/) {}
140
141
protected:
142
	YM2413Core() = default;
143
};
144
145
} // namespace openmsx
146
147
#endif