24 static MemBuffer<float, SSE_ALIGNMENT> mixBuffer;
25 static unsigned mixBufferSize = 0;
27 static void allocateMixBuffer(
unsigned size)
31 mixBuffer.
resize(mixBufferSize);
35 [[nodiscard]]
static string makeUnique(MSXMixer& mixer, std::string_view name)
38 if (mixer.findDevice(result)) {
41 result =
strCat(name,
" (", ++n,
')');
42 }
while (mixer.findDevice(result));
61 unsigned numChannels_,
unsigned inputRate,
bool stereo_)
63 , name(makeUnique(mixer, name_))
64 , description(description_)
65 , numChannels(numChannels_)
66 , stereo(stereo_ ? 2 : 1)
67 , numRecordChannels(0)
71 assert(stereo ==
one_of(1u, 2u));
84 return stereo == 2 || !balanceCenter;
89 return 1.0f / 32768.0f;
97 std::string_view mode = soundConfig.
getChildData(
"mode",
"mono");
100 }
else if (mode ==
"left") {
102 }
else if (mode ==
"right") {
108 for (
auto& b : soundConfig.
getChildren(
"balance")) {
109 auto balance = StringOp::stringTo<int>(b->getData());
111 throw MSXException(
"balance ", b->getData(),
" illegal");
114 if (!b->hasAttribute(
"channel")) {
115 devBalance = *balance;
120 if (*balance !=
one_of(0, -100, 100)) {
124 balanceCenter =
false;
127 const string& range = b->getAttribute(
"channel");
129 channelBalance[c - 1] = *balance;
133 mixer.registerSound(*
this, volume, devBalance, numChannels);
138 mixer.unregisterSound(*
this);
143 mixer.updateStream(time);
154 softwareVolumeLeft = left;
155 softwareVolumeRight = right;
156 mixer.updateSoftwareVolume(*
this);
161 assert(channel < numChannels);
162 bool wasRecording = writer[channel] !=
nullptr;
164 writer[channel] = std::make_unique<Wav16Writer>(
167 writer[channel].reset();
169 bool recording = writer[channel] !=
nullptr;
170 if (recording != wasRecording) {
172 if (numRecordChannels == 0) {
173 mixer.setSynchronousMode(
true);
176 assert(numRecordChannels <= numChannels);
178 assert(numRecordChannels > 0);
180 if (numRecordChannels == 0) {
181 mixer.setSynchronousMode(
false);
189 assert(channel < numChannels);
190 channelMuted[channel] = muted;
196 assert((uintptr_t(dataOut) & 15) == 0);
198 if (samples == 0)
return true;
199 unsigned outputStereo =
isStereo() ? 2 : 1;
201 static_assert(
sizeof(
float) ==
sizeof(uint32_t));
203 if (numChannels != 1) {
209 mSet(
reinterpret_cast<uint32_t*
>(dataOut), outputStereo * samples, 0);
213 unsigned separateChannels = 0;
214 unsigned pitch = (samples * stereo + 3) & ~3;
218 for (
auto i :
xrange(numChannels)) {
219 if (!channelMuted[i] && !writer[i] && balanceCenter) {
228 if (separateChannels) {
229 allocateMixBuffer(pitch * separateChannels);
230 mSet(
reinterpret_cast<uint32_t*
>(mixBuffer.
data()),
231 pitch * separateChannels, 0);
234 for (
auto i :
xrange(numChannels)) {
235 if (!(!channelMuted[i] && !writer[i] && balanceCenter)) {
236 bufs[i] = &mixBuffer[pitch *
count++];
239 assert(
count == separateChannels);
244 if (separateChannels == 0) {
246 [&](
auto i) {
return bufs[i]; });
250 for (
auto i :
xrange(numChannels)) {
252 assert(bufs[i] != dataOut);
256 bufs[i], stereo, samples,
260 writer[i]->writeSilence(stereo, samples);
266 bool anyUnmuted =
false;
268 VLA(
int, mixBalance, numChannels);
269 for (
auto i :
xrange(numChannels)) {
270 if (bufs[i] && !channelMuted[i]) {
272 if (bufs[i] != dataOut) {
273 bufs[numMix] = bufs[i];
274 mixBalance[numMix] = channelBalance[i];
286 if (!balanceCenter) {
295 if (mixBalance[j] <= 0) {
296 left0 += bufs[j][i + 0];
297 left1 += bufs[j][i + 1];
299 if (mixBalance[j] >= 0) {
300 right0 += bufs[j][i + 0];
301 right1 += bufs[j][i + 1];
304 }
while (j < numMix);
305 dataOut[i * 2 + 0] = left0;
306 dataOut[i * 2 + 1] = right0;
307 dataOut[i * 2 + 2] = left1;
308 dataOut[i * 2 + 3] = right1;
310 }
while (i < samples);
319 unsigned num = samples * stereo;
322 auto out0 = dataOut[i + 0];
323 auto out1 = dataOut[i + 1];
324 auto out2 = dataOut[i + 2];
325 auto out3 = dataOut[i + 3];
328 out0 += bufs[j][i + 0];
329 out1 += bufs[j][i + 1];
330 out2 += bufs[j][i + 2];
331 out3 += bufs[j][i + 3];
333 }
while (j < numMix);
334 dataOut[i + 0] = out0;
335 dataOut[i + 1] = out1;
336 dataOut[i + 2] = out2;
337 dataOut[i + 3] = out3;
346 return mixer.getHostSampleClock();
350 return mixer.getEffectiveSpeed();
const XMLElement & getChild(std::string_view name) const
Represents a clock with a variable frequency.
This class represents a filename.
const T * data() const
Returns pointer to the start of the memory buffer.
void resize(size_t size)
Grow or shrink the memory block.
double getEffectiveSpeed() const
std::pair< float, float > getAmplificationFactor() const
Gets this device its 'amplification factor'.
void recordChannel(unsigned channel, const Filename &filename)
virtual void generateChannels(float **buffers, unsigned num)=0
Abstract method to generate the actual sound data.
void updateStream(EmuTime::param time)
bool mixChannels(float *dataOut, unsigned samples)
Calls generateChannels() and combines the output to a single channel.
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.
static constexpr unsigned MAX_CHANNELS
void unregisterSound()
Unregisters this sound device with the Mixer.
bool isStereo() const
Is this a stereo device? This is set in the constructor and cannot be changed anymore.
SoundDevice(MSXMixer &mixer, std::string_view name, static_string_view description, unsigned numChannels, unsigned inputRate, bool stereo)
Constructor.
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.
const Children & getChildren() const
const std::string & getChildData(std::string_view childName) const
int getChildDataAsInt(std::string_view childName, int defaultValue=0) const
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
vector< unsigned > parseRange(string_view str, unsigned min, unsigned max)
This file implemented 3 utility functions:
constexpr const char *const filename
void fill(ForwardRange &&range, const T &value)
bool any_of(InputRange &&range, UnaryPredicate pred)
size_t size(std::string_view utf8)
std::string strCat(Ts &&...ts)
#define VLA(TYPE, NAME, LENGTH)
constexpr auto xrange(T e)