OXYGENHUB
◆ main openmsx / src / sound / MSXMixer.hh
5.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#ifndef MSXMIXER_HH
2
#define MSXMIXER_HH
3
4
#include "DynamicClock.hh"
5
#include "EmuTime.hh"
6
#include "InfoTopic.hh"
7
#include "Mixer.hh"
8
#include "Schedulable.hh"
9
10
#include "Observer.hh"
11
#include "dynarray.hh"
12
13
#include <memory>
14
#include <span>
15
#include <vector>
16
17
namespace openmsx {
18
19
class SoundDevice;
20
class Mixer;
21
class MSXMotherBoard;
22
class MSXCommandController;
23
class GlobalSettings;
24
class SpeedManager;
25
class ThrottleManager;
26
class IntegerSetting;
27
class StringSetting;
28
class BooleanSetting;
29
class Setting;
30
class AviRecorder;
31
32
class MSXMixer final : private Schedulable, private Observer<Setting>
33
                     , private Observer<SpeedManager>
34
                     , private Observer<ThrottleManager>
35
{
36
public:
37
	// See SoundDevice::getAmplificationFactor()
38
	// and MSXMixer::updateVolumeParams()
39
	static constexpr int AMP_BITS = 9;
40
41
	struct SoundDeviceInfo {
42
		explicit SoundDeviceInfo(unsigned numChannels);
43
44
		SoundDevice* device = nullptr;
45
		std::unique_ptr<IntegerSetting> volumeSetting;
46
		std::unique_ptr<IntegerSetting> balanceSetting;
47
		struct ChannelSettings {
48
			std::unique_ptr<StringSetting> record;
49
			std::unique_ptr<BooleanSetting> mute;
50
		};
51
		dynarray<ChannelSettings> channelSettings;
52
		float defaultVolume = 0.f;
53
		float left1 = 0.f, right1 = 0.f, left2 = 0.f, right2 = 0.f;
54
	};
55
56
public:
57
	MSXMixer(Mixer& mixer, MSXMotherBoard& motherBoard,
58
	         GlobalSettings& globalSettings);
59
	MSXMixer(const MSXMixer&) = delete;
60
	MSXMixer(MSXMixer&&) = delete;
61
	MSXMixer& operator=(const MSXMixer&) = delete;
62
	MSXMixer& operator=(MSXMixer&&) = delete;
63
	~MSXMixer();
64
65
	/**
66
	 * Use this method to register a given SoundDevice.
67
	 *
68
	 * While registering, the device its setSampleRate() method is
69
	 * called (see SoundDevice for more info).
70
	 * After registration the device its updateBuffer() method is
71
	 * 'regularly' called (see SoundDevice for more info).
72
	 */
73
	void registerSound(SoundDevice& device, float volume,
74
	                   int balance, unsigned numChannels);
75
76
	/**
77
	 * Every SoundDevice must unregister before it is destructed
78
	 */
79
	void unregisterSound(SoundDevice& device);
80
81
	/**
82
	 * Use this method to force an 'early' call to all
83
	 * updateBuffer() methods.
84
	 */
85
	void updateStream(EmuTime time);
86
87
	/**
88
	 * Used by SoundDevice::setSoftwareVolume()
89
	 */
90
	void updateSoftwareVolume(SoundDevice& device);
91
92
	/** Returns the ratio of EmuTime-speed per realtime-speed.
93
	 * In other words how many times faster EmuTime goes compared to
94
	 * realtime. This depends on the 'speed' setting but also on whether
95
	 * we're recording or not (in case of recording we want to generate
96
	 * sound as if realtime and EmuTime go at the same speed.
97
	 */
98
	[[nodiscard]] double getEffectiveSpeed() const;
99
100
	/** If we're recording, we want to emulate sound at 100% EmuTime speed.
101
	 * See also getEffectiveSpeed().
102
	 */
103
	void setSynchronousMode(bool synchronous);
104
	[[nodiscard]] bool isSynchronousMode() const { return synchronousCounter != 0; }
105
106
	/** TODO
107
	 * This methods (un)mute the sound.
108
	 * These methods may be called multiple times, as long as
109
	 * you never call unmute() more than mute()
110
	 */
111
	void mute();
112
	void unmute();
113
114
	// Called by Mixer or SoundDriver
115
116
	/** Set new fragment size and sample frequency.
117
	 * A fragment size of zero means the Mixer is muted.
118
	 */
119
	void setMixerParams(unsigned fragmentSize, unsigned sampleRate);
120
121
	/** Clock that ticks at the exact moment(s) in time that a host sample
122
	  * should be generated. The current time of this clock is the time of
123
	  * the last generated sample. The rate of this clock is the same as
124
	  * the host sample rate.
125
	  * Note that this rate is not the same as the frequency set with the
126
	  * 'frequency' setting. Either because the sound driver can't handle
127
	  * the requested speed or because the 'speed' setting is different
128
	  * from 100.
129
	  */
130
	[[nodiscard]] const DynamicClock& getHostSampleClock() const { return prevTime; }
131
132
	// Called by AviRecorder
133
	[[nodiscard]] bool needStereoRecording() const;
134
	void setRecorder(AviRecorder* recorder);
135
136
	// Returns the nominal host sample rate (not adjusted for speed setting)
137
	[[nodiscard]] unsigned getSampleRate() const { return hostSampleRate; }
138
139
	[[nodiscard]] SoundDevice* findDevice(std::string_view name) const;
140
	[[nodiscard]] const SoundDeviceInfo* findDeviceInfo(std::string_view name) const;
141
	[[nodiscard]] const auto& getDeviceInfos() const { return infos; }
142
143
	void reInit();
144
145
private:
146
	void updateVolumeParams(SoundDeviceInfo& info) const;
147
	void updateMasterVolume();
148
	void reschedule();
149
	void reschedule2();
150
	void generate(std::span<StereoFloat> output, EmuTime time);
151
152
	// Schedulable
153
	void executeUntil(EmuTime time) override;
154
155
	// Observer<Setting>
156
	void update(const Setting& setting) noexcept override;
157
	// Observer<SpeedManager>
158
	void update(const SpeedManager& speedManager) noexcept override;
159
	// Observer<ThrottleManager>
160
	void update(const ThrottleManager& throttleManager) noexcept override;
161
162
	void changeRecordSetting(const Setting& setting);
163
	void changeMuteSetting(const Setting& setting);
164
165
private:
166
	unsigned fragmentSize = 0;
167
	unsigned hostSampleRate = 44100; // requested freq by sound driver,
168
	                                 // not compensated for speed
169
170
	std::vector<SoundDeviceInfo> infos;
171
172
	Mixer& mixer;
173
	MSXMotherBoard& motherBoard;
174
	MSXCommandController& commandController;
175
176
	IntegerSetting& masterVolume;
177
	SpeedManager& speedManager;
178
	ThrottleManager& throttleManager;
179
180
	DynamicClock prevTime;
181
182
	struct SoundDeviceInfoTopic final : InfoTopic {
183
		explicit SoundDeviceInfoTopic(InfoCommand& machineInfoCommand);
184
		void execute(std::span<const TclObject> tokens,
185
			     TclObject& result) const override;
186
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
187
		void tabCompletion(std::vector<std::string>& tokens) const override;
188
	} soundDeviceInfo;
189
190
	AviRecorder* recorder = nullptr;
191
	unsigned synchronousCounter = 0;
192
193
	unsigned muteCount = 1; // start muted
194
	float tl0, tr0; // internal DC-filter state
195
};
196
197
} // namespace openmsx
198
199
#endif