22static MemBuffer<float, SSE_ALIGNMENT> mixBuffer;
23static size_t mixBufferSize = 0;
25static void allocateMixBuffer(
size_t size)
27 if (mixBufferSize <
size) [[unlikely]] {
29 mixBuffer.
resize(mixBufferSize);
33[[nodiscard]]
static std::string makeUnique(MSXMixer& mixer, std::string_view name)
35 std::string result(name);
36 if (mixer.findDevice(result)) {
39 result =
strCat(name,
" (", ++n,
')');
40 }
while (mixer.findDevice(result));
59 unsigned numChannels_,
unsigned inputRate,
bool stereo_)
61 , name(makeUnique(mixer, name_))
62 , description(description_)
63 , numChannels(numChannels_)
64 , stereo(stereo_ ? 2 : 1)
67 assert(stereo ==
one_of(1u, 2u));
80 return stereo == 2 || !balanceCenter;
85 return 1.0f / 32768.0f;
90 const auto& soundConfig = config.
getChild(
"sound");
91 float volume = narrow<float>(soundConfig.getChildDataAsInt(
"volume", 0)) * (1.0f / 32767.0f);
93 std::string_view mode = soundConfig.
getChildData(
"mode",
"mono");
96 }
else if (mode ==
"left") {
98 }
else if (mode ==
"right") {
104 for (
const auto* b : soundConfig.getChildren(
"balance")) {
105 auto balance = StringOp::stringTo<int>(b->getData());
107 throw MSXException(
"balance ", b->getData(),
" illegal");
110 const auto* channel = b->findAttribute(
"channel");
112 devBalance = *balance;
117 if (*balance !=
one_of(0, -100, 100)) {
121 balanceCenter =
false;
125 channels.foreachSetBit([&](
size_t c) {
126 channelBalance[c - 1] = *balance;
130 mixer.registerSound(*
this, volume, devBalance, numChannels);
135 mixer.unregisterSound(*
this);
140 mixer.updateStream(time);
151 softwareVolumeLeft = left;
152 softwareVolumeRight = right;
153 mixer.updateSoftwareVolume(*
this);
158 assert(channel < numChannels);
159 bool wasRecording = writer[channel].has_value();
160 if (!filename.
empty()) {
161 writer[channel].emplace(
162 filename, stereo, inputSampleRate);
164 writer[channel].reset();
166 bool recording = writer[channel].has_value();
167 if (recording != wasRecording) {
169 if (numRecordChannels == 0) {
170 mixer.setSynchronousMode(
true);
173 assert(numRecordChannels <= numChannels);
175 assert(numRecordChannels > 0);
177 if (numRecordChannels == 0) {
178 mixer.setSynchronousMode(
false);
186 assert(channel < numChannels);
187 channelMuted[channel] = muted;
193 assert((uintptr_t(dataOut) & 15) == 0);
195 if (samples == 0)
return true;
196 size_t outputStereo =
isStereo() ? 2 : 1;
198 std::array<float*, MAX_CHANNELS> bufs_;
199 auto bufs =
subspan(bufs_, 0, numChannels);
201 unsigned separateChannels = 0;
202 size_t pitch = (samples * stereo + 3) & ~3;
206 for (
auto i :
xrange(numChannels)) {
207 if (!channelMuted[i] && !writer[i] && balanceCenter) {
217 static_assert(
sizeof(float) ==
sizeof(uint32_t));
218 if ((numChannels != 1) || separateChannels) {
224 ranges::fill(std::span{dataOut, outputStereo * samples}, 0.0f);
227 if (separateChannels) {
228 allocateMixBuffer(pitch * separateChannels);
232 for (
auto i :
xrange(numChannels)) {
233 if (channelMuted[i] || writer[i] || !balanceCenter) {
234 bufs[i] = &mixBuffer[pitch * count++];
237 assert(count == separateChannels);
242 if (separateChannels == 0) {
244 [&](
auto i) {
return bufs[i]; });
248 for (
auto i :
xrange(numChannels)) {
250 assert(bufs[i] != dataOut);
255 std::span{bufs[i], samples},
259 std::span{
reinterpret_cast<const StereoFloat*
>(bufs[i]), samples},
260 amp.
left, amp.right);
263 writer[i]->writeSilence(narrow<unsigned>(stereo * samples));
269 bool anyUnmuted =
false;
271 VLA(
int, mixBalance, numChannels);
272 for (
auto i :
xrange(numChannels)) {
273 if (bufs[i] && !channelMuted[i]) {
275 if (bufs[i] != dataOut) {
276 bufs[numMix] = bufs[i];
277 mixBalance[numMix] = channelBalance[i];
289 if (!balanceCenter) {
298 if (mixBalance[j] <= 0) {
299 left0 += bufs[j][i + 0];
300 left1 += bufs[j][i + 1];
302 if (mixBalance[j] >= 0) {
303 right0 += bufs[j][i + 0];
304 right1 += bufs[j][i + 1];
307 }
while (j < numMix);
308 dataOut[i * 2 + 0] = left0;
309 dataOut[i * 2 + 1] = right0;
310 dataOut[i * 2 + 2] = left1;
311 dataOut[i * 2 + 3] = right1;
313 }
while (i < samples);
322 size_t num = samples * stereo;
325 auto out0 = dataOut[i + 0];
326 auto out1 = dataOut[i + 1];
327 auto out2 = dataOut[i + 2];
328 auto out3 = dataOut[i + 3];
331 out0 += bufs[j][i + 0];
332 out1 += bufs[j][i + 1];
333 out2 += bufs[j][i + 2];
334 out3 += bufs[j][i + 3];
336 }
while (j < numMix);
337 dataOut[i + 0] = out0;
338 dataOut[i + 1] = out1;
339 dataOut[i + 2] = out2;
340 dataOut[i + 3] = out3;
349 return mixer.getHostSampleClock();
353 return mixer.getEffectiveSpeed();
const XMLElement & getChild(std::string_view name) const
Represents a clock with a variable frequency.
This class represents a filename.
bool empty() const
Convenience method to test for empty filename.
void resize(size_t size)
Grow or shrink the memory block.
const T * data() const
Returns pointer to the start of the memory buffer.
double getEffectiveSpeed() const
void recordChannel(unsigned channel, const Filename &filename)
void updateStream(EmuTime::param time)
AmplificationFactors getAmplificationFactor() const
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().
void setInputRate(unsigned sampleRate)
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.
SoundDevice(const SoundDevice &)=delete
bool isStereo() const
Is this a stereo device? This is set in the constructor and cannot be changed anymore.
virtual void generateChannels(std::span< float * > buffers, unsigned num)=0
Abstract method to generate the actual sound data.
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
void muteChannel(unsigned channel, bool muted)
virtual float getAmplificationFactorImpl() const
Get amplification/attenuation factor for this device.
std::string_view getChildData(std::string_view childName) const
IterableBitSet< 64 > parseRange(string_view str, unsigned min, unsigned max)
This file implemented 3 utility functions:
bool any_of(InputRange &&range, UnaryPredicate pred)
constexpr void fill(ForwardRange &&range, const T &value)
size_t size(std::string_view utf8)
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
std::string strCat(Ts &&...ts)
#define VLA(TYPE, NAME, LENGTH)
constexpr auto xrange(T e)