OXYGENHUB
◆ main openmsx / src / sound / AY8910.hh
5.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef AY8910_HH
2
#define AY8910_HH
3
4
#include "ResampledSoundDevice.hh"
5
6
#include "FloatSetting.hh"
7
#include "SimpleDebuggable.hh"
8
#include "TclCallback.hh"
9
10
#include <array>
11
#include <cstdint>
12
13
namespace openmsx {
14
15
class AY8910Periphery;
16
class DeviceConfig;
17
18
/** This class implements the AY-3-8910 sound chip.
19
  * Only the AY-3-8910 is emulated, no surrounding hardware,
20
  * use the class AY8910Periphery to connect peripherals.
21
  */
22
class AY8910 final : public ResampledSoundDevice
23
{
24
public:
25
	AY8910(const std::string& name, AY8910Periphery& periphery,
26
	       const DeviceConfig& config, EmuTime time);
27
	~AY8910();
28
29
	[[nodiscard]] uint8_t readRegister(unsigned reg, EmuTime time);
30
	[[nodiscard]] uint8_t peekRegister(unsigned reg, EmuTime time) const;
31
	void writeRegister(unsigned reg, uint8_t value, EmuTime time);
32
	void reset(EmuTime time);
33
34
	template<typename Archive>
35
	void serialize(Archive& ar, unsigned version);
36
37
private:
38
	class Generator {
39
	public:
40
		void setPeriod(int value);
41
		[[nodiscard]] unsigned getNextEventTime() const;
42
		void advanceFast(unsigned duration);
43
44
		template<typename Archive>
45
		void serialize(Archive& ar, unsigned version);
46
47
	protected:
48
		Generator() = default;
49
		void reset();
50
51
		/** Time between output steps.
52
		  * For tones, this is half the period of the square wave.
53
		  * For noise, this is the time before the random generator produces
54
		  * its next output.
55
		  */
56
		int period;
57
		/** Time passed in this period.
58
		  * Usually count will be smaller than period, but when the period
59
		  * was recently changed this might not be the case.
60
		  */
61
		int count;
62
	};
63
64
	class ToneGenerator : public Generator {
65
	public:
66
		ToneGenerator();
67
68
		void reset();
69
70
		/** Advance tone generator several steps in time.
71
		  * @param duration Length of interval to simulate.
72
		  */
73
		void advance(unsigned duration);
74
75
		void doNextEvent(const AY8910& ay8910);
76
77
		/** Gets the current output of this generator.
78
		  */
79
		[[nodiscard]] bool getOutput() const { return output; }
80
81
		template<typename Archive>
82
		void serialize(Archive& ar, unsigned version);
83
84
	private:
85
		[[nodiscard]] int getDetune(const AY8910& ay8910);
86
87
	private:
88
		/** Time passed since start of vibrato cycle.
89
		  */
90
		unsigned vibratoCount = 0;
91
		unsigned detuneCount = 0;
92
93
		/** Current state of the wave.
94
		  */
95
		bool output;
96
	};
97
98
	class NoiseGenerator : public Generator {
99
	public:
100
		NoiseGenerator();
101
102
		void reset();
103
		/** Advance noise generator several steps in time.
104
		  * @param duration Length of interval to simulate.
105
		  */
106
		void advance(unsigned duration);
107
108
		void doNextEvent();
109
110
		/** Gets the current output of this generator.
111
		  */
112
		[[nodiscard]] bool getOutput() const { return random & 1; }
113
114
		template<typename Archive>
115
		void serialize(Archive& ar, unsigned version);
116
117
	private:
118
		int random;
119
	};
120
121
	class Amplitude {
122
	public:
123
		explicit Amplitude(const DeviceConfig& config);
124
		[[nodiscard]] auto getEnvVolTable() const { return envVolTable; }
125
		[[nodiscard]] float getVolume(unsigned chan) const;
126
		void setChannelVolume(unsigned chan, unsigned value);
127
		[[nodiscard]] bool followsEnvelope(unsigned chan) const;
128
129
	private:
130
		const bool isAY8910; // must come before envVolTable
131
		std::span<const float, 32> envVolTable;
132
		std::array<float, 3> vol;
133
		std::array<bool, 3> envChan;
134
	};
135
136
	class Envelope {
137
	public:
138
		explicit Envelope(std::span<const float, 32> envVolTable);
139
		void reset();
140
		void setPeriod(int value);
141
		void setShape(unsigned shape);
142
		[[nodiscard]] bool isChanging() const;
143
		void advance(unsigned duration);
144
		[[nodiscard]] float getVolume() const;
145
146
		[[nodiscard]] unsigned getNextEventTime() const;
147
		void advanceFast(unsigned duration);
148
		void doNextEvent();
149
150
		template<typename Archive>
151
		void serialize(Archive& ar, unsigned version);
152
153
	private:
154
		void doSteps(int steps);
155
156
	private:
157
		std::span<const float, 32> envVolTable;
158
		int period = 1;
159
		int count = 0;
160
		int step = 0;
161
		int attack = 0;
162
		bool hold = false, alternate = false, holding = false;
163
	};
164
165
	// SoundDevice
166
	void generateChannels(std::span<float*> bufs, unsigned num) override;
167
	[[nodiscard]] float getAmplificationFactorImpl() const override;
168
169
	// Observer<Setting>
170
	void update(const Setting& setting) noexcept override;
171
172
	void wrtReg(unsigned reg, uint8_t value, EmuTime time);
173
174
private:
175
	AY8910Periphery& periphery;
176
177
	struct Debuggable final : SimpleDebuggable {
178
		Debuggable(MSXMotherBoard& motherBoard, const std::string& name);
179
		[[nodiscard]] uint8_t read(unsigned address, EmuTime time) override;
180
		void write(unsigned address, uint8_t value, EmuTime time) override;
181
	} debuggable;
182
183
	FloatSetting vibratoPercent;
184
	FloatSetting vibratoFrequency;
185
	FloatSetting detunePercent;
186
	FloatSetting detuneFrequency;
187
	TclCallback directionsCallback;
188
	std::array<ToneGenerator, 3> tone;
189
	NoiseGenerator noise;
190
	Amplitude amplitude;
191
	Envelope envelope;
192
	std::array<uint8_t, 16> regs;
193
	const bool isAY8910;
194
	const bool ignorePortDirections;
195
	bool doDetune;
196
};
197
198
SERIALIZE_CLASS_VERSION(AY8910::Generator, 2);
199
SERIALIZE_CLASS_VERSION(AY8910::ToneGenerator, 2);
200
SERIALIZE_CLASS_VERSION(AY8910::NoiseGenerator, 2);
201
202
} // namespace openmsx
203
204
#endif // AY8910_HH