openMSX
SoundDevice.hh
Go to the documentation of this file.
1#ifndef SOUNDDEVICE_HH
2#define SOUNDDEVICE_HH
3
4#include "EmuTime.hh"
5#include "WavWriter.hh"
7#include <array>
8#include <optional>
9#include <string>
10#include <string_view>
11
12namespace openmsx {
13
14class DeviceConfig;
15class DynamicClock;
16class Filename;
17class MSXMixer;
18
20{
21public:
22 static constexpr unsigned MAX_CHANNELS = 24;
23
24 SoundDevice(const SoundDevice&) = delete;
28
32 [[nodiscard]] const std::string& getName() const { return name; }
33
37 [[nodiscard]] std::string_view getDescription() const { return description; }
38
41 [[nodiscard]] unsigned getNumChannels() const { return numChannels; }
42
45 [[nodiscard]] bool hasStereoChannels() const {
46 return stereo == 2;
47 }
48
51 [[nodiscard]] bool isStereo() const {
52 return hasStereoChannels() || !balanceCenter;
53 }
54
68 float left;
69 float right;
70 };
73 return {f * softwareVolumeLeft, f * softwareVolumeRight};
74 }
75
86 void setSoftwareVolume(float volume, EmuTime::param time);
87 void setSoftwareVolume(float left, float right, EmuTime::param time);
88
89 void recordChannel(unsigned channel, const Filename& filename);
90 void muteChannel (unsigned channel, bool muted);
91
98 void setBalance(unsigned channel, float balance);
99 virtual void postSetBalance();
100
116 [[nodiscard]] std::span<const float> getLastBuffer(unsigned channel);
117
120 [[nodiscard]] float getNativeSampleRate() const { return float(inputSampleRate); }
121
126 [[nodiscard]] unsigned getLastMonoBufferSize() const {
127 // Tuned so that both the ImGui waveform and spectrum viewer
128 // look 'nice' (and aren't too expensive to calculate).
129 return inputSampleRate / 7; // ~0.14 seconds
130 }
131 [[nodiscard]] unsigned getLastBufferSize() const {
132 return getLastMonoBufferSize() * stereo;
133 }
134
135protected:
144 SoundDevice(MSXMixer& mixer, std::string_view name, static_string_view description,
145 unsigned numChannels, unsigned inputRate, bool stereo);
147
156 [[nodiscard]] virtual float getAmplificationFactorImpl() const;
157
164 void registerSound(const DeviceConfig& config);
165
170 void unregisterSound();
171
173 void updateStream(EmuTime::param time);
174
175 void setInputRate(unsigned sampleRate) { inputSampleRate = sampleRate; }
176 [[nodiscard]] unsigned getInputRate() const { return inputSampleRate; }
177
178public: // Will be called by Mixer:
184 virtual void setOutputRate(unsigned hostSampleRate, double speed) = 0;
185
204 [[nodiscard]] virtual bool updateBuffer(size_t length, float* buffer,
205 EmuTime::param time) = 0;
206
207protected:
216 static void addFill(float*& buffer, float value, unsigned num);
217
231 virtual void generateChannels(std::span<float*> buffers, unsigned num) = 0;
232
245 [[nodiscard]] bool mixChannels(float* dataOut, size_t samples);
246
248 [[nodiscard]] const DynamicClock& getHostSampleClock() const;
249 [[nodiscard]] double getEffectiveSpeed() const;
250
251private:
252 struct Balance { // amplitude multiplication factors
253 float left, right;
254 };
255
256 MSXMixer& mixer;
257 const std::string name;
258 const static_string_view description;
259
260 std::array<std::optional<Wav16Writer>, MAX_CHANNELS> writer;
261
262 float softwareVolumeLeft = 1.0f;
263 float softwareVolumeRight = 1.0f;
264 unsigned inputSampleRate;
265 const unsigned numChannels;
266 const unsigned stereo;
267 unsigned numRecordChannels = 0;
268 std::array<Balance, MAX_CHANNELS> channelBalance;
269 std::array<bool, MAX_CHANNELS> channelMuted;
270 bool balanceCenter = true;
271
272 // When channel data needs to be collected separately (e.g. because
273 // we're recording the channel, or because we want to present it in the
274 // GUI), we store that data in this struct.
275 struct PerChannelBuffer {
276 std::vector<float> buffer; // dynamically grows when needed
277 unsigned stopIdx = 0; // buffer only contains valid data at indices [0, stopIdx)
278 unsigned requestCounter = 0; // != 0 means we're interested in collecting this data
279 unsigned silent = 999999; // how many 0 samples are at the end of the buffer
280 };
281 std::array<PerChannelBuffer, MAX_CHANNELS> channelBuffers = {};
282};
283
284} // namespace openmsx
285
286#endif
Represents a clock with a variable frequency.
This class represents a filename.
Definition Filename.hh:20
double getEffectiveSpeed() const
SoundDevice & operator=(const SoundDevice &)=delete
std::string_view getDescription() const
Gets a description of this sound device, to be presented to the user.
virtual bool updateBuffer(size_t length, float *buffer, EmuTime::param time)=0
Generate sample data.
unsigned getNumChannels() const
How many channels does this device have?
void recordChannel(unsigned channel, const Filename &filename)
bool hasStereoChannels() const
Are the individual channels of this device stereo?
void updateStream(EmuTime::param time)
unsigned getInputRate() const
SoundDevice & operator=(SoundDevice &&)=delete
unsigned getLastBufferSize() const
AmplificationFactors getAmplificationFactor() const
const std::string & getName() const
Get the unique name that identifies this sound device.
float getNativeSampleRate() const
The samples returned by 'getLastBuffer()' have this sample rate.
static void addFill(float *&buffer, float value, unsigned num)
Adds a number of samples that all have the same value.
const DynamicClock & getHostSampleClock() const
See MSXMixer::getHostSampleClock().
SoundDevice(SoundDevice &&)=delete
void setInputRate(unsigned sampleRate)
virtual void setOutputRate(unsigned hostSampleRate, double speed)=0
When a SoundDevice registers itself with the Mixer, the Mixer sets the required sampleRate through th...
void setSoftwareVolume(float volume, EmuTime::param time)
Change the 'software volume' of this sound device.
bool mixChannels(float *dataOut, size_t samples)
Calls generateChannels() and combines the output to a single channel.
static constexpr unsigned MAX_CHANNELS
void unregisterSound()
Unregisters this sound device with the Mixer.
unsigned getLastMonoBufferSize() const
getLastBuffer() with return buffers containing this many samples.
SoundDevice(const SoundDevice &)=delete
bool isStereo() const
Is the full output of this device stereo?
virtual void generateChannels(std::span< float * > buffers, unsigned num)=0
Abstract method to generate the actual sound data.
void setBalance(unsigned channel, float balance)
Change the balance of a single channel.
virtual void postSetBalance()
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
std::span< const float > getLastBuffer(unsigned channel)
Query the last generated audio signal for a specific channel.
void muteChannel(unsigned channel, bool muted)
virtual float getAmplificationFactorImpl() const
Get amplification/attenuation factor for this device.
static_string_view
This file implemented 3 utility functions:
Definition Autofire.cc:11
Gets this device its 'amplification factor'.